Add test suite, fix a mountain of small errors.
diff --git a/.gitignore b/.gitignore
index b4479a6..df66a77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
CMakeCache.txt
CPackConfig.cmake
CPackSourceConfig.cmake
+*.cmake
Makefile
*.bin
*.dump
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bf5ddc4..e10590c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,11 @@
cmake_minimum_required(VERSION 3.10)
include(FetchContent)
+include(GoogleTest)
project(CPERParse)
+# GoogleTest requires at least C++14.
+set(CMAKE_CXX_STANDARD 14)
+
# Output into subdirectories /lib and /bin.
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
@@ -21,7 +25,14 @@
GIT_REPOSITORY https://github.com/jwerle/b64.c.git
GIT_TAG c33188cd541f19b072ee4988d8224ea6c964bed1 # 9/2/2021
)
-FetchContent_MakeAvailable(json-c b64-c)
+
+# Fetch GoogleTest from git repository.
+FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG release-1.12.1
+)
+FetchContent_MakeAvailable(json-c b64-c googletest)
# Add library and test executable.
file(GLOB SectionSources sections/*.c)
@@ -29,13 +40,31 @@
file(GLOB GeneratorSectionSources generator/sections/*.c)
add_library(cper-parse STATIC cper-parse.c ir-parse.c cper-utils.c json-schema.c json-schema.h ${SectionSources} ${EDKSources})
add_executable(cper-convert cli-app/cper-convert.c)
-add_executable(cper-generate generator/cper-generate.c generator/gen-utils.c ${GeneratorSectionSources} ${EDKSources})
+add_executable(cper-generate
+ generator/cper-generate-cli.c
+ generator/cper-generate.c
+ generator/gen-utils.c
+ ${GeneratorSectionSources}
+ ${EDKSources}
+)
+add_executable(cper-tests
+ tests/ir-tests.cpp
+ tests/test-utils.cpp
+ generator/cper-generate.c
+ generator/gen-utils.c
+ ${GeneratorSectionSources}
+ ${EDKSources}
+)
# Link library.
+target_link_libraries(cper-tests cper-parse json-c GTest::gtest_main)
target_link_libraries(cper-parse json-c b64c)
target_link_libraries(cper-convert cper-parse)
target_compile_options(cper-parse PRIVATE -Wno-address-of-packed-member)
# Copy required specification JSON for command line application.
add_custom_command(TARGET cper-convert POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory bin/specification)
-add_custom_command(TARGET cper-convert POST_BUILD COMMAND cp -r specification/json/* bin/specification)
\ No newline at end of file
+add_custom_command(TARGET cper-convert POST_BUILD COMMAND cp -r specification/json/* bin/specification)
+
+# Add tests to GoogleTest.
+#gtest_discover_tests(cper-tests WORKING_DIRECTORY bin/)
\ No newline at end of file
diff --git a/cli-app/cper-convert.c b/cli-app/cper-convert.c
index ba1908d..488b6c9 100644
--- a/cli-app/cper-convert.c
+++ b/cli-app/cper-convert.c
@@ -111,17 +111,12 @@
//Are we skipping validation?
int do_validate = 1;
- if (argc == 6)
+ if (argc >= 6 && argc < 8)
{
if (strcmp(argv[5], "--no-validate") == 0)
{
do_validate = 0;
}
- else
- {
- printf("Invalid argument '%s' for command 'to-cper'. See 'cper-convert --help' for command information.\n", argv[5]);
- return;
- }
}
//Validate the JSON against specification, unless otherwise specified.
@@ -129,19 +124,12 @@
{
char* specification_path = NULL;
- //If there is another argument pair, it must be a validation specification file path.
- if (argc == 7)
+ //Is there a specification file path?
+ if (argc >= 7 && strcmp(argv[argc - 2], "--specification") == 0)
{
- //Ensure valid argument naming.
- if (strcmp(argv[5], "--specification") != 0)
- {
- printf("Invalid argument '%s' for command 'to-cper'. See 'cper-convert --help' for command information.\n", argv[5]);
- return;
- }
-
- specification_path = argv[6];
+ specification_path = argv[argc - 1];
}
- else if (argc == 5)
+ else
{
//Make the specification path the assumed default (application directory + specification/cper-json.json).
specification_path = malloc(PATH_MAX);
@@ -149,11 +137,16 @@
strcpy(specification_path, dir);
strcat(specification_path, "/specification/cper-json.json");
}
- else
+
+ //Enable debug mode if indicated.
+ for (int i=5; i<argc; i++)
{
- //Invalid number of arguments.
- printf("Invalid number of arguments for command 'to-cper'. See 'cper-convert --help' for command information.\n");
- return;
+ if (strcmp(argv[i], "--debug") == 0)
+ {
+ validate_schema_debug_enable();
+ printf("debug enabled.\n");
+ break;
+ }
}
//Attempt to verify with the the specification.
@@ -194,7 +187,7 @@
printf(":: to-json cper.file [--out file.name]\n");
printf("\tConverts the provided CPER log file into JSON, by default outputting to console. If '--out' is specified,\n");
printf("\tThe outputted JSON will be written to the provided file name instead.\n");
- printf("\n:: to-cper cper.json --out file.name [--no-validate] [--specification some/spec/path.json]\n");
+ printf("\n:: to-cper cper.json --out file.name [--no-validate] [--debug] [--specification some/spec/path.json]\n");
printf("\tConverts the provided CPER-JSON JSON file into CPER binary. An output file must be specified with '--out'.\n");
printf("\tBy default, the provided JSON will try to be validated against a specification. If no specification file path\n");
printf("\tis provided with '--specification', then it will default to 'argv[0] + /specification/cper-json.json'.\n");
diff --git a/cper.generated.json b/cper.generated.json
new file mode 100644
index 0000000..af3bcc8
--- /dev/null
+++ b/cper.generated.json
@@ -0,0 +1,222 @@
+{
+ "header":{
+ "revision":{
+ "major":0,
+ "minor":0
+ },
+ "sectionCount":1,
+ "severity":{
+ "code":2,
+ "name":"Corrected"
+ },
+ "validationBits":{
+ "platformIDValid":true,
+ "timestampValid":false,
+ "partitionIDValid":false
+ },
+ "recordLength":716,
+ "platformID":"00000000-0000-0000-0000000000000000",
+ "creatorID":"00000000-0000-0000-0000000000000000",
+ "notificationType":{
+ "guid":"00000000-0000-0000-0000000000000000",
+ "type":"Unknown"
+ },
+ "recordID":1783804367,
+ "flags":{
+ "value":4,
+ "name":"HW_ERROR_FLAGS_SIMULATED"
+ },
+ "persistenceInfo":0
+ },
+ "sectionDescriptors":[
+ {
+ "sectionOffset":200,
+ "sectionLength":516,
+ "revision":{
+ "major":41,
+ "minor":73
+ },
+ "validationBits":{
+ "fruIDValid":false,
+ "fruStringValid":true
+ },
+ "flags":{
+ "primary":false,
+ "containmentWarning":false,
+ "reset":false,
+ "errorThresholdExceeded":false,
+ "resourceNotAccessible":false,
+ "latentError":true,
+ "propagated":true,
+ "overflow":false
+ },
+ "sectionType":{
+ "data":"dc3ea0b0-a144-4797-b95b53fa242b6e1d",
+ "type":"IA32\/X64"
+ },
+ "fruText":"\rgæ\rw Ü\u0010\u000f$ÿ¨óþ´Kî\tÍvtY]'",
+ "severity":{
+ "code":3,
+ "name":"Informational"
+ }
+ }
+ ],
+ "sections":[
+ {
+ "validationBits":{
+ "localAPICIDValid":true,
+ "cpuIDInfoValid":false,
+ "processorErrorInfoNum":3,
+ "processorContextInfoNum":1
+ },
+ "localAPICID":11973824614848415809,
+ "cpuidInfo":{
+ "eax":13368524000736729030,
+ "ebx":16886328965158955209,
+ "ecx":3305063700665067796,
+ "edx":1435640360842869561
+ },
+ "processorErrorInfo":[
+ {
+ "type":"a55701f5-e3ef-43de-ac72249b573fad2c",
+ "validationBits":{
+ "checkInfoValid":false,
+ "targetAddressIDValid":true,
+ "requestorIDValid":false,
+ "responderIDValid":false,
+ "instructionPointerValid":false
+ },
+ "checkInfo":{
+ "validationBits":{
+ "transactionTypeValid":false,
+ "operationValid":true,
+ "levelValid":false,
+ "processorContextCorruptValid":true,
+ "uncorrectedValid":false,
+ "preciseIPValid":false,
+ "restartableIPValid":false,
+ "overflowValid":true
+ },
+ "transactionType":{
+ "value":1,
+ "name":"Data Access"
+ },
+ "operation":{
+ "value":6,
+ "name":"Eviction"
+ },
+ "level":4,
+ "processorContextCorrupt":false,
+ "uncorrected":true,
+ "preciseIP":true,
+ "restartableIP":false,
+ "overflow":true
+ },
+ "targetAddressID":0,
+ "requestorID":0,
+ "responderID":0,
+ "instructionPointer":0
+ },
+ {
+ "type":"1cf3f8b3-c5b1-49a2-aa595eef92ffa63c",
+ "validationBits":{
+ "checkInfoValid":false,
+ "targetAddressIDValid":false,
+ "requestorIDValid":false,
+ "responderIDValid":false,
+ "instructionPointerValid":true
+ },
+ "checkInfo":{
+ "validationBits":{
+ "transactionTypeValid":true,
+ "operationValid":true,
+ "levelValid":false,
+ "processorContextCorruptValid":true,
+ "uncorrectedValid":false,
+ "preciseIPValid":false,
+ "restartableIPValid":true,
+ "overflowValid":true,
+ "participationTypeValid":true,
+ "timedOutValid":false,
+ "addressSpaceValid":false
+ },
+ "transactionType":{
+ "value":3,
+ "name":"Unknown (Reserved)"
+ },
+ "operation":{
+ "value":3,
+ "name":"Data Write"
+ },
+ "level":6,
+ "processorContextCorrupt":true,
+ "uncorrected":false,
+ "preciseIP":false,
+ "restartableIP":true,
+ "overflow":false,
+ "timedOut":false,
+ "participationType":{
+ "value":3,
+ "name":"Generic"
+ },
+ "addressSpace":{
+ "value":2,
+ "name":"I\/O"
+ }
+ },
+ "targetAddressID":6868879,
+ "requestorID":0,
+ "responderID":0,
+ "instructionPointer":0
+ },
+ {
+ "type":"48ab7f57-dc34-4f6c-a7d3b0b5b0a74314",
+ "validationBits":{
+ "checkInfoValid":true,
+ "targetAddressIDValid":true,
+ "requestorIDValid":true,
+ "responderIDValid":true,
+ "instructionPointerValid":true
+ },
+ "checkInfo":{
+ "validationBits":{
+ "errorTypeValid":false,
+ "processorContextCorruptValid":false,
+ "uncorrectedValid":false,
+ "preciseIPValid":false,
+ "restartableIPValid":false,
+ "overflowValid":false
+ },
+ "errorType":{
+ "value":0,
+ "name":"No Error"
+ },
+ "processorContextCorrupt":false,
+ "uncorrected":false,
+ "preciseIP":false,
+ "restartableIP":false,
+ "overflow":false
+ },
+ "targetAddressID":0,
+ "requestorID":0,
+ "responderID":0,
+ "instructionPointer":3883791728653631488
+ }
+ ],
+ "processorContextInfo":[
+ {
+ "registerContextType":{
+ "value":59519,
+ "name":"Unknown (Reserved)"
+ },
+ "registerArraySize":21123,
+ "msrAddress":346845773,
+ "mmRegisterAddress":1449923871159163716,
+ "registerArray":{
+ "data":"rOuS7It1ghdUVMSLhfwTBOWWVjIJAkZNHRN5Mj2YRumD2dUOTlcmoqvqLTDmQDTL14v94I1ELatXpt2VPiN+wfxU0Eqr9uxX4BmHxlq8kjFHjxHU0z5\/K+VcwCOAPuV8krXHPquzlYvNHFEn2ONYH3Np9Eanc3GM0DGwUHCVzAJKk0D1R9WAFPLRO8q1k+oo\/N5uo1HgMCER4HGBdT6Ev9HEtBiaNCyMBWdWuvpA4vYeUZpwAAAAAEEAAAAAAAAABAAAAAEAAACNe3FdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBOaBdvVQAAAAAAAAAAAABBAAAAAAAAABAAAAAEAAAACFBoF29VAAAYT2gXb1UAAKBOaBdvVQAANH5xXSd\/AABBFnJdJ38AAK0Wcl0nfwAAkQIAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBWaBdvVQAAAAAAAAAAAADgVWgXb1UAAPhQaBdvVQAACFBoF29VAABQv2gXb1UAAAAAAAAAAAAA8FpoF29VAAAAAAAAAAAAAPhQaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAVWgXb1UAAAAAAAAAAAAAMFFoF29VAADwTmgXb1UAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQWmgXb1UAAAAAAAAAAAAAQFZoF29VAAAYT2gXb1UAAPBOaBdvVQAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwUWgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAABAAAAJBTaBdvVQAAuFNoF29VAACwUWgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4FRoF29VAAAAAAAAAAAAAKBUaBdvVQAAaFNoF29VAACQU2gXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAVWgXb1UAAAAAAAAAAAAAAFVoF29VAAC4U2gXb1UAAKBSaBdvVQAAgFRoF29VAAAAAAAAAAAAAEBUaBdvVQAAoFJoF29VAAAAAAAAAAAAAKBVaBdvVQAAAAAAAAAAAABgVWgXb1UAAAAAAAAAAAAAaFNoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAbG9jYWxBUElDSURWYWxpZAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAY3B1SURJbmZvVmFsaWQAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAACEAAAAAAAAAcHJvY2Vzc29yRXJyb3JJbmZvTnVtAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAACEAAAAAAAAAcHJvY2Vzc29yQ29udGV4dEluZm9OdW0AIQAAAAAAAAB2YWxpZGF0aW9uQml0cwAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEFI3hIMkiumIQAAAAAAAABsb2NhbEFQSUNJRAAAAAAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAVmgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAABAAAAIhXaBdvVQAAsFdoF29VAADAVmgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBaaBdvVQAAAAAAAAAAAAAQWmgXb1UAALBXaBdvVQAAAFhoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQWWgXb1UAAAAAAAAAAAAAUFloF29VAAAAWGgXb1UAAAAAAAAAAAAAsFpoF29VAAAAAAAAAAAAAHBaaBdvVQAAAAAAAAAAAADoVmgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwWWgXb1UAAAAAAAAAAAAAsFloF29VAADoVmgXb1UAAIhXaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAADGVzLLv4mGuSEAAAAAAAAAZWF4AAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAADJaLDouUpY6iEAAAAAAAAAZWJ4AAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAU6S2+7PHdLSEAAAAAAAAAZWN4AAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAA5u0BFTWvsEyEAAAAAAAAAZWR4AAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABjcHVpZEluZm8AAAAAAAAAAAAAAAAAAABBAAAAAAAAAAUAAAABAAAAA51xXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwW2gXb1UAAAAAAAAAAAAAMQAAAAAAAABgW2gXb1UAAAMAAAAAAAAAIAAAAAAAAAD9nnFdJ38AAAAAAAAAAAAAEQEAAAAAAABwXGgXb1UAAEB6aBdvVQAAoKZoF29VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwXGgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAABwAAAGhdaBdvVQAAGF1oF29VAADwXGgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAAYHloF29VAAAAAAAAAAAAACB5aBdvVQAAgF5oF29VAAAgX2gXb1UAACB6aBdvVQAAAAAAAAAAAADgeWgXb1UAAAAAAAAAAAAAgF5oF29VAACgeGgXb1UAAAAAAAAAAAAAEGVoF29VAAAgX2gXb1UAAFheaBdvVQAA4F9oF29VAAAAAAAAAAAAAIBfaBdvVQAAWF5oF29VAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8GRoF29VAAAAAAAAAAAAAABgaBdvVQAAQF1oF29VAABoXWgXb1UAAMB5aBdvVQAAAAAAAAAAAACAeWgXb1UAABhdaBdvVQAA8FxoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeWgXb1UAAAAAAAAAAAAAwHhoF29VAADwXGgXb1UAAEBdaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYQAAAAAAAAAGAAAAAQAAAIuYcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIwAAAAAAAABhNTU3MDFmNS1lM2VmLTQzZGUtYWM3MjI0OWI1NzNmYWQyYwAAAAAAIQAAAAAAAAB0eXBlAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAYGgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAABQAAAEhhaBdvVQAAmGFoF29VAACAYGgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQY2gXb1UAAAAAAAAAAAAAEGNoF29VAADAYWgXb1UAAAAAAAAAAAAAcGRoF29VAAAAAAAAAAAAADBkaBdvVQAAmGFoF29VAAAQYmgXb1UAANBkaBdvVQAAAAAAAAAAAACQZGgXb1UAAAAAAAAAAAAAcGFoF29VAACwY2gXb1UAAAAAAAAAAAAAcGNoF29VAAAQYmgXb1UAAEhhaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBkaBdvVQAAAAAAAAAAAADQY2gXb1UAAHBhaBdvVQAAwGFoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAY2hlY2tJbmZvVmFsaWQAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAdGFyZ2V0QWRkcmVzc0lEVmFsaWQAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcmVxdWVzdG9ySURWYWxpZAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcmVzcG9uZGVySURWYWxpZAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAaW5zdHJ1Y3Rpb25Qb2ludGVyVmFsaWQAIQAAAAAAAAB2YWxpZGF0aW9uQml0cwAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQZWgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAACQAAAPhmaBdvVQAA4GVoF29VAACQZWgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAeGgXb1UAAAAAAAAAAAAAQHhoF29VAAAAAAAAAAAAAHBnaBdvVQAAQHZoF29VAAAAAAAAAAAAAGByaBdvVQAAwGdoF29VAACAZmgXb1UAAAB3aBdvVQAAAAAAAAAAAADAdmgXb1UAAFhmaBdvVQAAwGdoF29VAABgd2gXb1UAAAAAAAAAAAAAIHdoF29VAADQZmgXb1UAADBmaBdvVQAAQHJoF29VAAAAAAAAAAAAAGBuaBdvVQAACGZoF29VAAD4ZmgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAd2gXb1UAAAAAAAAAAAAAgHdoF29VAABwZ2gXb1UAAFhmaBdvVQAAQG5oF29VAAAAAAAAAAAAACBoaBdvVQAAgGZoF29VAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHhoF29VAAAAAAAAAAAAAOB3aBdvVQAA4GVoF29VAADQZmgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgdmgXb1UAAAAAAAAAAAAAYHZoF29VAAAwZmgXb1UAAAhmaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAAEAAAAAQAAAI17cV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGhoF29VAAAAAAAAAAAAAEEAAAAAAAAAEAAAAAgAAABYamgXb1UAAKhqaBdvVQAAoGhoF29VAAA0fnFdJ38AAEEWcl0nfwAArRZyXSd\/AACRAgAAAAAAAGBtaBdvVQAAAAAAAAAAAAAgbWgXb1UAAIBqaBdvVQAAkGloF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtaBdvVQAAAAAAAAAAAADAbGgXb1UAAKBoaBdvVQAA4GloF29VAAAwbGgXb1UAAAAAAAAAAAAA8GtoF29VAADgaWgXb1UAAPhqaBdvVQAAkGxoF29VAAAAAAAAAAAAAFBsaBdvVQAAkGloF29VAAC4aWgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGtoF29VAAAAAAAAAAAAADBraBdvVQAA+GpoF29VAAAAAAAAAAAAAMBtaBdvVQAAAAAAAAAAAACAbWgXb1UAAKhqaBdvVQAAoGhoF29VAAAgbmgXb1UAAAAAAAAAAAAA4G1oF29VAAAAAAAAAAAAAIBqaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANBraBdvVQAAAAAAAAAAAACQa2gXb1UAALhpaBdvVQAAWGpoF29VAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAHRyYW5zYWN0aW9uVHlwZVZhbGlkAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAG9wZXJhdGlvblZhbGlkAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAGxldmVsVmFsaWQAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAxAAAAAAAAAHByb2Nlc3NvckNvbnRleHRDb3JydXB0VmFsaWQAAAAAAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAB1bmNvcnJlY3RlZFZhbGlkAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABwcmVjaXNlSVBWYWxpZAAAAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAByZXN0YXJ0YWJsZUlQVmFsaWQAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABvdmVyZmxvd1ZhbGlkAAAAAAAAAAAAAAAhAAAAAAAAAHZhbGlkYXRpb25CaXRzAAAAAAAAAAAAAEEAAAAAAAAABAAAAAEAAACNe3FdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKBuaBdvVQAAAAAAAAAAAABBAAAAAAAAABAAAAACAAAASHBoF29VAAAIb2gXb1UAAOBuaBdvVQAANH5xXSd\/AABBFnJdJ38AAK0Wcl0nfwAAkQIAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHJoF29VAAAAAAAAAAAAANBxaBdvVQAAAAAAAAAAAABIcGgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwcWgXb1UAAAAAAAAAAAAAcHFoF29VAAAIb2gXb1UAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAIQAAAAAAAAB2YWx1ZQAAAAAAAAAAAAAAAAAAAAAAAABRAAAAAAAAAAYAAAABAAAAi5hxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAERhdGEgQWNjZXNzAAAAAAAAAAAAAAAAACEAAAAAAAAAbmFtZQAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAB0cmFuc2FjdGlvblR5cGUAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgcmgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAAAgAAAEh0aBdvVQAACHNoF29VAADgcmgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2aBdvVQAAAAAAAAAAAADQdWgXb1UAAAAAAAAAAAAASHRoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHVoF29VAAAAAAAAAAAAAHB1aBdvVQAACHNoF29VAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAACEAAAAAAAAAdmFsdWUAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAGAAAAAQAAAIuYcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAABFdmljdGlvbgAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAG5hbWUAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAb3BlcmF0aW9uAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAAAAAAAAACEAAAAAAAAAbGV2ZWwAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcHJvY2Vzc29yQ29udGV4dENvcnJ1cHQAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAdW5jb3JyZWN0ZWQAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcHJlY2lzZUlQAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcmVzdGFydGFibGVJUAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAb3ZlcmZsb3cAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABjaGVja0luZm8AAAAAAAAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAB0YXJnZXRBZGRyZXNzSUQAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAByZXF1ZXN0b3JJRAAAAAAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAByZXNwb25kZXJJRAAAAAAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABpbnN0cnVjdGlvblBvaW50ZXIAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAemgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAABwAAADh7aBdvVQAA6HpoF29VAADAemgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAAwKVoF29VAAAAAAAAAAAAAIClaBdvVQAAUHxoF29VAADwfGgXb1UAAICmaBdvVQAAAAAAAAAAAABApmgXb1UAAAAAAAAAAAAAUHxoF29VAABApWgXb1UAAAAAAAAAAAAA4IJoF29VAADwfGgXb1UAACh8aBdvVQAAsH1oF29VAAAAAAAAAAAAAFB9aBdvVQAAKHxoF29VAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwIJoF29VAAAAAAAAAAAAANB9aBdvVQAAEHtoF29VAAA4e2gXb1UAACCmaBdvVQAAAAAAAAAAAADgpWgXb1UAAOh6aBdvVQAAwHpoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgpWgXb1UAAAAAAAAAAAAA8J9oF29VAADAemgXb1UAABB7aBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYQAAAAAAAAAGAAAAAQAAAIuYcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIwAAAAAAAAAxY2YzZjhiMy1jNWIxLTQ5YTItYWE1OTVlZWY5MmZmYTYzYwAAAAAAIQAAAAAAAAB0eXBlAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQfmgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAABQAAABh\/aBdvVQAAaH9oF29VAABQfmgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAggWgXb1UAAAAAAAAAAAAA4IBoF29VAACQf2gXb1UAAAAAAAAAAAAAQIJoF29VAAAAAAAAAAAAAACCaBdvVQAAaH9oF29VAADgf2gXb1UAAKCCaBdvVQAAAAAAAAAAAABggmgXb1UAAAAAAAAAAAAAQH9oF29VAACAgWgXb1UAAAAAAAAAAAAAQIFoF29VAADgf2gXb1UAABh\/aBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCBaBdvVQAAAAAAAAAAAACggWgXb1UAAEB\/aBdvVQAAkH9oF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAY2hlY2tJbmZvVmFsaWQAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAdGFyZ2V0QWRkcmVzc0lEVmFsaWQAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcmVxdWVzdG9ySURWYWxpZAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcmVzcG9uZGVySURWYWxpZAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAaW5zdHJ1Y3Rpb25Qb2ludGVyVmFsaWQAIQAAAAAAAAB2YWxpZGF0aW9uQml0cwAAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgg2gXb1UAAAAAAAAAAAAAQQAAAAAAAAAgAAAADAAAAJihaBdvVQAAiKJoF29VAAAwoGgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAAAAAAAAAAAAAQAGgXb1UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwl2gXb1UAAAAAAAAAAAAAMJdoF29VAABohWgXb1UAAECFaBdvVQAAMJVoF29VAAAAAAAAAAAAAFCRaBdvVQAAkIVoF29VAABQhGgXb1UAAPCVaBdvVQAAAAAAAAAAAACwlWgXb1UAACiEaBdvVQAAkIVoF29VAABQlmgXb1UAAAAAAAAAAAAAEJZoF29VAACghGgXb1UAAACEaBdvVQAAMJFoF29VAAAAAAAAAAAAAFCNaBdvVQAA2INoF29VAADIhGgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwlmgXb1UAAAAAAAAAAAAAcJZoF29VAABAhWgXb1UAACiEaBdvVQAAMI1oF29VAAAAAAAAAAAAAPCFaBdvVQAAUIRoF29VAAAAAAAAAAAAANCbaBdvVQAAAAAAAAAAAADwl2gXb1UAAAAAAAAAAAAAaIVoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJdoF29VAAAAAAAAAAAAANCWaBdvVQAAsINoF29VAACghGgXb1UAANCXaBdvVQAAAAAAAAAAAACQl2gXb1UAAPCEaBdvVQAAsINoF29VAACQlWgXb1UAAAAAAAAAAAAAUJVoF29VAAAAhGgXb1UAANiDaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAAEAAAAAQAAAI17cV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMIZoF29VAAAAAAAAAAAAAEEAAAAAAAAAEAAAAAsAAAAoiGgXb1UAAJiGaBdvVQAAcIZoF29VAAA0fnFdJ38AAEEWcl0nfwAArRZyXSd\/AACRAgAAAAAAADCLaBdvVQAAAAAAAAAAAADwimgXb1UAAFCIaBdvVQAAYIdoF29VAAAQjWgXb1UAAAAAAAAAAAAA0IxoF29VAAAAAAAAAAAAADiHaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCMaBdvVQAAAAAAAAAAAAAQjGgXb1UAADiHaBdvVQAAeIhoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsIxoF29VAAAAAAAAAAAAAHCMaBdvVQAAmIZoF29VAADohmgXb1UAANCKaBdvVQAAAAAAAAAAAACQimgXb1UAAHCGaBdvVQAAsIdoF29VAAAAimgXb1UAAAAAAAAAAAAAwIloF29VAACwh2gXb1UAAMiIaBdvVQAAYIpoF29VAAAAAAAAAAAAACCKaBdvVQAAYIdoF29VAACIh2gXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIloF29VAAAAAAAAAAAAAACJaBdvVQAAyIhoF29VAAAAAAAAAAAAAJCLaBdvVQAAAAAAAAAAAABQi2gXb1UAAHiIaBdvVQAAcIZoF29VAADwi2gXb1UAAAAAAAAAAAAAsItoF29VAADohmgXb1UAAFCIaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCJaBdvVQAAAAAAAAAAAABgiWgXb1UAAIiHaBdvVQAAKIhoF29VAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAHRyYW5zYWN0aW9uVHlwZVZhbGlkAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAG9wZXJhdGlvblZhbGlkAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAGxldmVsVmFsaWQAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAQAAAAEAAAC1gnFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAxAAAAAAAAAHByb2Nlc3NvckNvbnRleHRDb3JydXB0VmFsaWQAAAAAAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAB1bmNvcnJlY3RlZFZhbGlkAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABwcmVjaXNlSVBWYWxpZAAAAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAByZXN0YXJ0YWJsZUlQVmFsaWQAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABvdmVyZmxvd1ZhbGlkAAAAAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABwYXJ0aWNpcGF0aW9uVHlwZVZhbGlkAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAB0aW1lZE91dFZhbGlkAAAAAAAAAAAAAABBAAAAAAAAAAEAAAABAAAAtYJxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABhZGRyZXNzU3BhY2VWYWxpZAAAAAAAAAAhAAAAAAAAAHZhbGlkYXRpb25CaXRzAAAAAAAAAAAAAEEAAAAAAAAABAAAAAEAAACNe3FdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCNaBdvVQAAAAAAAAAAAABBAAAAAAAAABAAAAACAAAAOI9oF29VAAD4jWgXb1UAANCNaBdvVQAANH5xXSd\/AABBFnJdJ38AAK0Wcl0nfwAAkQIAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJFoF29VAAAAAAAAAAAAAMCQaBdvVQAAAAAAAAAAAAA4j2gXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgkGgXb1UAAAAAAAAAAAAAYJBoF29VAAD4jWgXb1UAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAMAAAAAAAAAIQAAAAAAAAB2YWx1ZQAAAAAAAAAAAAAAAAAAAAAAAABRAAAAAAAAAAYAAAABAAAAi5hxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAAAAAAFVua25vd24gKFJlc2VydmVkKQAAAAAAACEAAAAAAAAAbmFtZQAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAAB0cmFuc2FjdGlvblR5cGUAAAAAAAAAAABBAAAAAAAAAAQAAAABAAAAjXtxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQkWgXb1UAAAAAAAAAAAAAQQAAAAAAAAAQAAAAAgAAADiTaBdvVQAA+JFoF29VAADQkWgXb1UAADR+cV0nfwAAQRZyXSd\/AACtFnJdJ38AAJECAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCVaBdvVQAAAAAAAAAAAADAlGgXb1UAAAAAAAAAAAAAOJNoF29VAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoJRoF29VAAAAAAAAAAAAAGCUaBdvVQAA+JFoF29VAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAACEAAAAAAAAAdmFsdWUAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAGAAAAAQAAAIuYcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAABEYXRhIFdyaXRlAAAAAAAAAAAAAAAAAAAhAAAAAAAAAG5hbWUAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAb3BlcmF0aW9uAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAADAAAAAQAAALuEcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAACEAAAAAAAAAbGV2ZWwAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcHJvY2Vzc29yQ29udGV4dENvcnJ1cHQAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAdW5jb3JyZWN0ZWQAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcHJlY2lzZUlQAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACEAAAAAAAAAcmVzdGFydGFibGVJUAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAb3ZlcmZsb3cAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAABAAAAAQAAALWCcV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAdGltZWRPdXQAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAAEAAAAAQAAAI17cV0nfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJhoF29VAAAAAAAAAAAAAEEAAAAAAAAAEAAAAAIAAADYmWgXb1UAAJiYaBdvVQAAcJhoF29VAAA0fnFdJ38AAEEWcl0nfwAArRZyXSd\/AACRAgAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwm2gXb1UAAAAAAAAAAAAAYJtoF29VAAAAAAAAAAAAANiZaBdvVQAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECbaBdvVQAAAAAAAAAAAAAAm2gXb1UAAJiYaBdvVQAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAwAAAAEAAAC7hHFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAAAAAAhAAAAAAAAAHZhbHVlAAAAAAAAAAAAAAAAAAAAAAAAAFEAAAAAAAAABgAAAAEAAACLmHFdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAR2VuZXJpYwAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABuYW1lAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAAAHBhcnRpY2lwYXRpb25UeXBlAAAAAAAAAEEAAAAAAAAABAAAAAEAAACNe3FdJ38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCcaBdvVQAAAAAAAAAAAABBAAAAAAAAABAAAAACAAAA2J1oF29VAACYnGgXb1UAAHCcaBdvVQAANH5xXSd\/AABBFnJdJ38AAK0Wcl0nfwAAkQIAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsJ9oF29VAAAAAAAAAAAAAGCfaBdvVQAAAAAAAAAAAADYnWgXb1UAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAn2gXb1UAAAAAAAAAAAAAAJ9oF29VAACYnGgXb1UAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAMAAAABAAAAu4RxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAIQAAAAAAAAB2YWx1ZQAAAAAAAAAAAAAAAAAAAAAAAABRAAAAAAAAAAYAAAABAAAAi5hxXSd\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAEkvTwAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAbmFtZQAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABhZGRyZXNzU3BhY2UAAAAAAAAA"
+ }
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/generator/cper-generate-cli.c b/generator/cper-generate-cli.c
new file mode 100644
index 0000000..0b17efe
--- /dev/null
+++ b/generator/cper-generate-cli.c
@@ -0,0 +1,70 @@
+/**
+ * A user-space application for generating psuedo-random specification compliant CPER records.
+ *
+ * Author: Lawrence.Tang@arm.com
+ **/
+
+#include <stdio.h>
+#include <string.h>
+#include "../edk/Cper.h"
+#include "cper-generate.h"
+
+void print_help();
+
+int main(int argc, char* argv[])
+{
+ //If help requested, print help.
+ if (argc == 2 && strcmp(argv[1], "--help") == 0)
+ {
+ print_help();
+ return 0;
+ }
+
+ //Ensure the minimum number of arguments.
+ if (argc < 5)
+ {
+ printf("Insufficient number of arguments. See 'cper-generate --help' for command information.\n");
+ return -1;
+ }
+
+ //Open a file handle to write output.
+ FILE* cper_file = fopen(argv[2], "w");
+ if (cper_file == NULL)
+ {
+ printf("Could not get a handle for output file '%s', file handle returned null.\n", argv[2]);
+ return -1;
+ }
+
+ //Generate the record. Type names start from argv[4].
+ UINT16 num_sections = argc - 4;
+ generate_cper_record(argv + 4, num_sections, cper_file);
+ fclose(cper_file);
+}
+
+
+//Prints command help for this CPER generator.
+void print_help()
+{
+ printf(":: --out cper.file --sections section1 [section2 section3 ...]\n");
+ printf("\tGenerates a psuedo-random CPER file with the provided section types and outputs to the given file name.\n");
+ printf("\tValid section type names are the following:\n");
+ printf("\t\t- generic\n");
+ printf("\t\t- ia32x64\n");
+ printf("\t\t- ipf\n");
+ printf("\t\t- arm\n");
+ printf("\t\t- memory\n");
+ printf("\t\t- memory2\n");
+ printf("\t\t- pcie\n");
+ printf("\t\t- firmware\n");
+ printf("\t\t- pcibus\n");
+ printf("\t\t- pcidev\n");
+ printf("\t\t- dmargeneric\n");
+ printf("\t\t- dmarvtd\n");
+ printf("\t\t- dmariommu\n");
+ printf("\t\t- ccixper\n");
+ printf("\t\t- cxlprotocol\n");
+ printf("\t\t- cxlcomponent\n");
+ printf("\t\t- unknown\n");
+ printf("\n:: --help\n");
+ printf("\tDisplays help information to the console.\n");
+}
\ No newline at end of file
diff --git a/generator/cper-generate.c b/generator/cper-generate.c
index 23fad1d..93df4fe 100644
--- a/generator/cper-generate.c
+++ b/generator/cper-generate.c
@@ -1,5 +1,5 @@
/**
- * A user-space application for generating psuedo-random specification compliant CPER records.
+ * Describes functions for generating psuedo-random specification compliant CPER records.
*
* Author: Lawrence.Tang@arm.com
**/
@@ -10,41 +10,28 @@
#include "../edk/Cper.h"
#include "gen-utils.h"
#include "sections/gen-sections.h"
+#include "cper-generate.h"
EFI_ERROR_SECTION_DESCRIPTOR* generate_section_descriptor(char* type, size_t* lengths, int index, int num_sections);
size_t generate_section(void** location, char* type);
-void print_help();
-int main(int argc, char* argv[])
+//Generates a CPER record with the given section types, outputting to the given stream.
+void generate_cper_record(char** types, UINT16 num_sections, FILE* out)
{
- //If help requested, print help.
- if (argc == 2 && strcmp(argv[1], "--help") == 0)
- {
- print_help();
- return 0;
- }
-
- //Ensure the minimum number of arguments.
- if (argc < 5)
- {
- printf("Insufficient number of arguments. See 'cper-generate --help' for command information.\n");
- return -1;
- }
-
//Initialise randomiser.
init_random();
- //Generate the sections. Type names start from argv[4].
- UINT16 num_sections = argc - 4;
+ //Generate the sections.
void* sections[num_sections];
size_t section_lengths[num_sections];
for (int i=0; i<num_sections; i++)
{
- section_lengths[i] = generate_section(sections + i, argv[4 + i]);
+ section_lengths[i] = generate_section(sections + i, types[i]);
if (section_lengths[i] == 0)
{
//Error encountered, exit.
- return -1;
+ printf("Error encountered generating section %d of type '%s', length returned zero.\n", i+1, types[i]);
+ return;
}
}
@@ -63,7 +50,7 @@
//Generate the section descriptors given the number of sections.
EFI_ERROR_SECTION_DESCRIPTOR* section_descriptors[num_sections];
for (int i=0; i<num_sections; i++)
- section_descriptors[i] = generate_section_descriptor(argv[4 + i], section_lengths, i, num_sections);
+ section_descriptors[i] = generate_section_descriptor(types[i], section_lengths, i, num_sections);
//Calculate total length of structure, set in header.
size_t total_len = sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
@@ -72,31 +59,22 @@
total_len += num_sections * sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
header->RecordLength = (UINT32)total_len;
- //Open a file handle to write output.
- FILE* cper_file = fopen(argv[2], "w");
- if (cper_file == NULL)
- {
- printf("Could not get a handle for output file '%s', file handle returned null.\n", argv[2]);
- return -1;
- }
-
- //Write to file in order, free all resources.
- fwrite(header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1, cper_file);
- fflush(cper_file);
+ //Write to stream in order, free all resources.
+ fwrite(header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1, out);
+ fflush(out);
free(header);
for (int i=0; i<num_sections; i++)
{
- fwrite(section_descriptors[i], sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1, cper_file);
- fflush(cper_file);
+ fwrite(section_descriptors[i], sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1, out);
+ fflush(out);
free(section_descriptors[i]);
}
for (int i=0; i<num_sections; i++)
{
- fwrite(sections[i], section_lengths[i], 1, cper_file);
- fflush(cper_file);
+ fwrite(sections[i], section_lengths[i], 1, out);
+ fflush(out);
free(sections[i]);
}
- fclose(cper_file);
}
//Generates a single section descriptor for a section with the given properties.
@@ -110,6 +88,9 @@
descriptor->Resv1 = 0;
descriptor->SectionFlags &= 0xFF;
+ //Set severity.
+ descriptor->Severity = rand() % 4;
+
//Set length, offset from base record.
descriptor->SectionLength = (UINT32)lengths[index];
descriptor->SectionOffset = sizeof(EFI_COMMON_ERROR_RECORD_HEADER)
@@ -230,31 +211,4 @@
}
return length;
-}
-
-//Prints command help for this CPER generator.
-void print_help()
-{
- printf(":: --out cper.file --sections section1 [section2 section3 ...]\n");
- printf("\tGenerates a psuedo-random CPER file with the provided section types and outputs to the given file name.\n");
- printf("\tValid section type names are the following:\n");
- printf("\t\t- generic\n");
- printf("\t\t- ia32x64\n");
- printf("\t\t- ipf\n");
- printf("\t\t- arm\n");
- printf("\t\t- memory\n");
- printf("\t\t- memory2\n");
- printf("\t\t- pcie\n");
- printf("\t\t- firmware\n");
- printf("\t\t- pcibus\n");
- printf("\t\t- pcidev\n");
- printf("\t\t- dmargeneric\n");
- printf("\t\t- dmarvtd\n");
- printf("\t\t- dmariommu\n");
- printf("\t\t- ccixper\n");
- printf("\t\t- cxlprotocol\n");
- printf("\t\t- cxlcomponent\n");
- printf("\t\t- unknown\n");
- printf("\n:: --help\n");
- printf("\tDisplays help information to the console.\n");
}
\ No newline at end of file
diff --git a/generator/cper-generate.h b/generator/cper-generate.h
new file mode 100644
index 0000000..213cf8c
--- /dev/null
+++ b/generator/cper-generate.h
@@ -0,0 +1,9 @@
+#ifndef CPER_GENERATE_H
+#define CPER_GENERATE_H
+
+#include <stdio.h>
+#include "../edk/BaseTypes.h"
+
+void generate_cper_record(char** types, UINT16 num_sections, FILE* cper_file);
+
+#endif
\ No newline at end of file
diff --git a/generator/sections/gen-section-ccix-per.c b/generator/sections/gen-section-ccix-per.c
index a61983b..074fd0d 100644
--- a/generator/sections/gen-section-ccix-per.c
+++ b/generator/sections/gen-section-ccix-per.c
@@ -28,6 +28,10 @@
UINT16* reserved = (UINT16*)(bytes + 14);
*reserved = 0; //Reserved bytes 14-15.
+ //Set expected values.
+ UINT32* length = (UINT32*)bytes;
+ *length = size;
+
//Set return values, exit.
*location = bytes;
return size;
diff --git a/generator/sections/gen-section-ia32x64.c b/generator/sections/gen-section-ia32x64.c
index 7412ca3..a9b41e4 100644
--- a/generator/sections/gen-section-ia32x64.c
+++ b/generator/sections/gen-section-ia32x64.c
@@ -75,25 +75,25 @@
{
//Cache
case 0:
- memcpy(guid, &gEfiIa32x64ErrorTypeCacheCheckGuid, sizeof(guid));
+ memcpy(guid, &gEfiIa32x64ErrorTypeCacheCheckGuid, sizeof(EFI_GUID));
memset(error_structure + 30, 0, 34);
break;
//TLB
case 1:
- memcpy(guid, &gEfiIa32x64ErrorTypeTlbCheckGuid, sizeof(guid));
+ memcpy(guid, &gEfiIa32x64ErrorTypeTlbCheckGuid, sizeof(EFI_GUID));
memset(error_structure + 30, 0, 34);
break;
//Bus
case 2:
- memcpy(guid, &gEfiIa32x64ErrorTypeBusCheckGuid, sizeof(guid));
+ memcpy(guid, &gEfiIa32x64ErrorTypeBusCheckGuid, sizeof(EFI_GUID));
memset(error_structure + 35, 0, 29);
break;
//MS
case 3:
- memcpy(guid, &gEfiIa32x64ErrorTypeMsCheckGuid, sizeof(guid));
+ memcpy(guid, &gEfiIa32x64ErrorTypeMsCheckGuid, sizeof(EFI_GUID));
memset(error_structure + 24, 0, 38);
break;
}
diff --git a/generator/sections/gen-section-pci-dev.c b/generator/sections/gen-section-pci-dev.c
index 1882d6c..f6a8bcc 100644
--- a/generator/sections/gen-section-pci-dev.c
+++ b/generator/sections/gen-section-pci-dev.c
@@ -27,6 +27,12 @@
*validation &= 0b11111; //Validation 5-63
for (int i=0; i<5; i++)
*(bytes + 27 + i) = 0; //Bytes 11-15 of ID info.
+
+ //Set expected values.
+ UINT32* memory_number_field = (UINT32*)(bytes + 32);
+ UINT32* io_number_field = (UINT32*)(bytes + 36);
+ *memory_number_field = num_memory_pairs;
+ *io_number_field = num_io_pairs;
//Fix error status.
create_valid_error_section(bytes + 8);
diff --git a/json-schema.c b/json-schema.c
index bb3b7fe..4762dff 100644
--- a/json-schema.c
+++ b/json-schema.c
@@ -86,6 +86,8 @@
chdir(original_cwd);
free(original_cwd);
+ if (result)
+ log_validator_debug("Successfully validated the provided object against schema.");
return result;
}
diff --git a/sections/cper-section-generic.h b/sections/cper-section-generic.h
index 4a60791..f1e180e 100644
--- a/sections/cper-section-generic.h
+++ b/sections/cper-section-generic.h
@@ -11,7 +11,7 @@
#define GENERIC_ERROR_TYPES_KEYS (int []){0, 1, 2, 4, 8}
#define GENERIC_ERROR_TYPES_VALUES (const char*[]){"Unknown", "Cache Error", "TLB Error", "Bus Error", "Micro-Architectural Error"}
#define GENERIC_OPERATION_TYPES_KEYS (int []){0, 1, 2, 3}
-#define GENERIC_OPERATION_TYPES_VALUES (const char*[]){"Unknown or generic", "Data Read", "Data Write", "Instruction Execution"}
+#define GENERIC_OPERATION_TYPES_VALUES (const char*[]){"Unknown or Generic", "Data Read", "Data Write", "Instruction Execution"}
#define GENERIC_VALIDATION_BITFIELD_NAMES (const char*[]) \
{"processorTypeValid", "processorISAValid", "processorErrorTypeValid", "operationValid", "flagsValid", \
"levelValid", "cpuVersionValid", "cpuBrandInfoValid", "cpuIDValid", "targetAddressValid", "requestorIDValid", \
diff --git a/sections/cper-section-ia32x64.c b/sections/cper-section-ia32x64.c
index 784a25c..8c39ea8 100644
--- a/sections/cper-section-ia32x64.c
+++ b/sections/cper-section-ia32x64.c
@@ -99,17 +99,26 @@
//Add the check information on a per-structure basis.
//Cache and TLB check information are identical, so can be equated.
- json_object* checkInformation = NULL;
+ json_object* check_information = NULL;
if (guid_equal(&error_info->ErrorType, &gEfiIa32x64ErrorTypeCacheCheckGuid)
|| guid_equal(&error_info->ErrorType, &gEfiIa32x64ErrorTypeTlbCheckGuid))
{
- checkInformation = cper_ia32x64_cache_tlb_check_to_ir((EFI_IA32_X64_CACHE_CHECK_INFO*)&error_info->CheckInfo);
+ check_information = cper_ia32x64_cache_tlb_check_to_ir((EFI_IA32_X64_CACHE_CHECK_INFO*)&error_info->CheckInfo);
}
else if (guid_equal(&error_info->ErrorType, &gEfiIa32x64ErrorTypeBusCheckGuid))
- checkInformation = cper_ia32x64_bus_check_to_ir((EFI_IA32_X64_BUS_CHECK_INFO*)&error_info->CheckInfo);
+ {
+ check_information = cper_ia32x64_bus_check_to_ir((EFI_IA32_X64_BUS_CHECK_INFO*)&error_info->CheckInfo);
+ }
else if (guid_equal(&error_info->ErrorType, &gEfiIa32x64ErrorTypeMsCheckGuid))
- checkInformation = cper_ia32x64_ms_check_to_ir((EFI_IA32_X64_MS_CHECK_INFO*)&error_info->CheckInfo);
- json_object_object_add(error_info_ir, "checkInfo", checkInformation);
+ {
+ check_information = cper_ia32x64_ms_check_to_ir((EFI_IA32_X64_MS_CHECK_INFO*)&error_info->CheckInfo);
+ }
+ else
+ {
+ //Unknown check information.
+ printf("WARN: Invalid/unknown check information GUID found in IA32/x64 CPER section. Ignoring.\n");
+ }
+ json_object_object_add(error_info_ir, "checkInfo", check_information);
//Target, requestor, and responder identifiers.
json_object_object_add(error_info_ir, "targetAddressID", json_object_new_uint64(error_info->TargetId));
@@ -269,7 +278,7 @@
//No parseable data, just dump as base64 and shift the head to the next item.
*cur_pos = (void*)(context_info + 1);
- char* encoded = b64_encode((unsigned char*)cur_pos, context_info->ArraySize);
+ char* encoded = b64_encode((unsigned char*)*cur_pos, context_info->ArraySize);
register_array = json_object_new_object();
json_object_object_add(register_array, "data", json_object_new_string(encoded));
free(encoded);
diff --git a/sections/cper-section-ia32x64.h b/sections/cper-section-ia32x64.h
index d8c6825..a18e751 100644
--- a/sections/cper-section-ia32x64.h
+++ b/sections/cper-section-ia32x64.h
@@ -8,8 +8,8 @@
{"checkInfoValid", "targetAddressIDValid", "requestorIDValid", "responderIDValid", \
"instructionPointerValid"}
#define IA32X64_CHECK_INFO_VALID_BITFIELD_NAMES (const char*[]) \
- {"transactionTypeValid", "operationValid", "levelValid", "processorContextCorruptValid", "uncorrectedValid" \
- "preciseIPValid", "restartableIPValid", "overflowValid", "participationTypeValid", "timedOutValid" \
+ {"transactionTypeValid", "operationValid", "levelValid", "processorContextCorruptValid", "uncorrectedValid", \
+ "preciseIPValid", "restartableIPValid", "overflowValid", "participationTypeValid", "timedOutValid", \
"addressSpaceValid"}
#define IA32X64_CHECK_INFO_MS_CHECK_VALID_BITFIELD_NAMES (const char*[]) \
{"errorTypeValid", "processorContextCorruptValid", "uncorrectedValid", "preciseIPValid", "restartableIPValid", \
diff --git a/specification/document/cper-json-specification.tex b/specification/document/cper-json-specification.tex
index 3b261a5..eb71d45 100644
--- a/specification/document/cper-json-specification.tex
+++ b/specification/document/cper-json-specification.tex
@@ -285,7 +285,7 @@
\hline
processorISAValid & boolean & Whether the "processorISA" field of the Generic Processor Error section (\ref{section:genericprocessorerrorsection}) is valid.\\
\hline
-errorTypeValid & boolean & Whether the "errorType" field of the Generic Processor Error section (\ref{section:genericprocessorerrorsection}) is valid.\\
+processorErrorTypeValid & boolean & Whether the "errorType" field of the Generic Processor Error section (\ref{section:genericprocessorerrorsection}) is valid.\\
\hline
operationValid & boolean & Whether the "operation" field of the Generic Processor Error section (\ref{section:genericprocessorerrorsection}) is valid.\\
\hline
@@ -511,6 +511,8 @@
participationTypeValid & boolean (\textbf{optional}) & Whether the "participationType" field in the Processor Error Check Info (Bus Error) structure (\ref{subsection:ia32x64processorerrorcheckinfobusstructure}) is valid. \textbf{This field is only present on bus related check info structures.}\\
\hline
timedOutValid & boolean (\textbf{optional}) & Whether the "timeOut" field in the Processor Error Check Info (Bus Error) structure (\ref{subsection:ia32x64processorerrorcheckinfobusstructure}) is valid. \textbf{This field is only present on bus related check info structures.}\\
+\hline
+addressSpaceValid & boolean (\textbf{optional}) & Whether the "addressSpace" field in the Processor Error Check Info (Bus Error) structure (\ref{subsection:ia32x64processorerrorcheckinfobusstructure}) is valid. \textbf{This field is only present on bus related check info structures.}\\
\jsontableend{IA32/x64 Processor Error Check Info (Cache/TLB/Bus) validation structure field table.}
% IA32/x64 Processor Error Check Info (MS Check) Validation structure.
diff --git a/specification/json/sections/cper-generic-processor.json b/specification/json/sections/cper-generic-processor.json
index d30719c..0a5d52b 100644
--- a/specification/json/sections/cper-generic-processor.json
+++ b/specification/json/sections/cper-generic-processor.json
@@ -7,7 +7,7 @@
"properties": {
"validationBits": {
"type": "object",
- "required": ["processorTypeValid", "processorISAValid", "errorTypeValid", "operationValid", "flagsValid", "levelValid", "cpuVersionValid", "cpuBrandInfoValid", "cpuIDValid", "targetAddressValid", "requestorIDValid", "responderIDValid", "instructionIPValid"],
+ "required": ["processorTypeValid", "processorISAValid", "processorErrorTypeValid", "operationValid", "flagsValid", "levelValid", "cpuVersionValid", "cpuBrandInfoValid", "cpuIDValid", "targetAddressValid", "requestorIDValid", "responderIDValid", "instructionIPValid"],
"properties": {
"processorTypeValid": {
"type": "boolean"
@@ -15,7 +15,7 @@
"processorISAValid": {
"type": "boolean"
},
- "errorTypeValid": {
+ "processorErrorTypeValid": {
"type": "boolean"
},
"operationValid": {
@@ -68,9 +68,9 @@
},
"flags": {
"type": "object",
- "required": ["restartableIP", "preciseIP", "overflow", "corrected"],
+ "required": ["restartable", "preciseIP", "overflow", "corrected"],
"properties": {
- "restartableIP": {
+ "restartable": {
"type": "boolean"
},
"preciseIP": {
diff --git a/specification/json/sections/cper-ia32x64-processor.json b/specification/json/sections/cper-ia32x64-processor.json
index 33e6511..11aa819 100644
--- a/specification/json/sections/cper-ia32x64-processor.json
+++ b/specification/json/sections/cper-ia32x64-processor.json
@@ -152,7 +152,7 @@
"properties": {
"validationBits": {
"type": "object",
- "required": ["transactionTypeValid", "operationValid", "levelValid", "processorContextCorruptValid", "uncorrectedValid", "preciseIPValid", "restartableIPValid", "overflowValid", "participationTypeValid", "timedOutValid"],
+ "required": ["transactionTypeValid", "operationValid", "levelValid", "processorContextCorruptValid", "uncorrectedValid", "preciseIPValid", "restartableIPValid", "overflowValid", "participationTypeValid", "timedOutValid", "addressSpaceValid"],
"properties": {
"transactionTypeValid": {
"type": "boolean"
@@ -183,6 +183,9 @@
},
"timedOutValid": {
"type": "boolean"
+ },
+ "addressSpaceValid": {
+ "type": "boolean"
}
}
},
diff --git a/tests/ir-tests.cpp b/tests/ir-tests.cpp
new file mode 100644
index 0000000..1344379
--- /dev/null
+++ b/tests/ir-tests.cpp
@@ -0,0 +1,97 @@
+/**
+ * Defines tests for validating CPER-JSON IR output from the cper-parse library.
+ *
+ * Author: Lawrence.Tang@arm.com
+ **/
+
+#include "gtest/gtest.h"
+#include "test-utils.hpp"
+extern "C" {
+#include "json.h"
+#include "../cper-parse.h"
+#include "../json-schema.h"
+#include "../generator/cper-generate.h"
+}
+
+/*
+* Test templates.
+*/
+void single_section_ir_test(const char* section_name)
+{
+ //Generate CPER record for generic processor.
+ char* buf;
+ size_t size;
+ FILE* record = generate_record_memstream(§ion_name, 1, &buf, &size);
+
+ //Convert to IR, free resources.
+ json_object* ir = cper_to_ir(record);
+ fclose(record);
+ free(buf);
+
+ //Validate against schema.
+ char error_message[JSON_ERROR_MSG_MAX_LEN] = {0};
+ int valid = validate_schema_from_file("./specification/cper-json.json", ir, error_message);
+ ASSERT_TRUE(valid) << error_message;
+}
+
+/*
+* Single section tests.
+*/
+TEST(GenericProcessorTests, IRValid) {
+ single_section_ir_test("generic");
+}
+TEST(IA32x64Tests, IRValid) {
+ single_section_ir_test("ia32x64");
+}
+// TEST(IPFTests, IRValid) {
+// single_section_ir_test("ipf");
+// }
+TEST(ArmTests, IRValid) {
+ single_section_ir_test("arm");
+}
+TEST(MemoryTests, IRValid) {
+ single_section_ir_test("memory");
+}
+TEST(Memory2Tests, IRValid) {
+ single_section_ir_test("memory2");
+}
+TEST(PCIeTests, IRValid) {
+ single_section_ir_test("pcie");
+}
+TEST(FirmwareTests, IRValid) {
+ single_section_ir_test("firmware");
+}
+TEST(PCIBusTests, IRValid) {
+ single_section_ir_test("pcibus");
+}
+TEST(PCIDevTests, IRValid) {
+ single_section_ir_test("pcidev");
+}
+TEST(DMArGenericTests, IRValid) {
+ single_section_ir_test("dmargeneric");
+}
+TEST(DMArVtdTests, IRValid) {
+ single_section_ir_test("dmarvtd");
+}
+TEST(DMArIOMMUTests, IRValid) {
+ single_section_ir_test("dmariommu");
+}
+TEST(CCIXPERTests, IRValid) {
+ single_section_ir_test("ccixper");
+}
+TEST(CXLProtocolTests, IRValid) {
+ single_section_ir_test("cxlprotocol");
+}
+TEST(CXLComponentTests, IRValid) {
+ single_section_ir_test("cxlcomponent");
+}
+TEST(UnknownSectionTests, IRValid) {
+ single_section_ir_test("unknown");
+}
+
+//Entrypoint for the testing program.
+int main()
+{
+ testing::InitGoogleTest();
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp
new file mode 100644
index 0000000..0d4cabf
--- /dev/null
+++ b/tests/test-utils.cpp
@@ -0,0 +1,27 @@
+/**
+ * Defines utility functions for testing CPER-JSON IR output from the cper-parse library.
+ *
+ * Author: Lawrence.Tang@arm.com
+ **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "test-utils.hpp"
+extern "C" {
+#include "../edk/BaseTypes.h"
+#include "../generator/cper-generate.h"
+}
+
+//Returns a ready-for-use memory stream containing a CPER record with the given sections inside.
+FILE* generate_record_memstream(const char** types, UINT16 num_types, char** buf, size_t* buf_size)
+{
+ //Open a memory stream.
+ FILE* stream = open_memstream(buf, buf_size);
+
+ //Generate a section to the stream, close & return.
+ generate_cper_record((char**)types, num_types, stream);
+ fclose(stream);
+
+ //Return fmemopen() buffer for reading.
+ return fmemopen(*buf, *buf_size, "r");
+}
\ No newline at end of file
diff --git a/tests/test-utils.hpp b/tests/test-utils.hpp
new file mode 100644
index 0000000..d13f5f0
--- /dev/null
+++ b/tests/test-utils.hpp
@@ -0,0 +1,10 @@
+#ifndef CPER_IR_TEST_UTILS_H
+#define CPER_IR_TEST_UTILS_H
+
+extern "C" {
+#include "../edk/BaseTypes.h"
+}
+
+FILE* generate_record_memstream(const char** types, UINT16 num_types, char** buf, size_t* buf_size);
+
+#endif
\ No newline at end of file