Add IR header conversion.
diff --git a/.gitignore b/.gitignore
index 1e16a39..b4479a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
CMakeFiles/
bin/
lib/
+samples/
.vscode/
cmake_install.cmake
CMakeCache.txt
@@ -9,6 +10,7 @@
CPackSourceConfig.cmake
Makefile
*.bin
+*.dump
specification/document/*.out
specification/document/*.pdf
specification/document/*.toc
diff --git a/cper-utils.c b/cper-utils.c
index 88d7aad..454fc5b 100644
--- a/cper-utils.c
+++ b/cper-utils.c
@@ -106,6 +106,13 @@
return result;
}
+//Returns a single UINT64 value from the given readable pair object.
+//Assumes the integer value is held in the "value" field.
+UINT64 readable_pair_to_integer(json_object* pair)
+{
+ return json_object_get_uint64(json_object_object_get(pair, "value"));
+}
+
//Converts the given 64 bit bitfield to IR, assuming bit 0 starts on the left.
json_object* bitfield_to_ir(UINT64 bitfield, int num_fields, const char* names[])
{
@@ -118,6 +125,19 @@
return result;
}
+//Converts the given IR bitfield into a standard UINT64 bitfield, with fields beginning from bit 0.
+UINT64 ir_to_bitfield(json_object* ir, int num_fields, const char* names[])
+{
+ UINT64 result = 0x0;
+ for (int i=0; i<num_fields; i++)
+ {
+ if (json_object_get_boolean(json_object_object_get(ir, names[i])))
+ result |= (0x1 << i);
+ }
+
+ return result;
+}
+
//Converts the given UINT64 array into a JSON IR array, given the length.
json_object* uint64_array_to_ir_array(UINT64* array, int len)
{
@@ -142,6 +162,33 @@
return severity < 4 ? CPER_SEVERITY_TYPES[severity] : "Unknown";
}
+//Converts a single EFI timestamp to string, at the given output.
+//Output must be at least TIMESTAMP_LENGTH bytes long.
+void timestamp_to_string(char* out, EFI_ERROR_TIME_STAMP* timestamp)
+{
+ sprintf(out, "%02d%02d-%02d-%02dT%02d:%02d:%02d.000",
+ timestamp->Century,
+ timestamp->Year,
+ timestamp->Month,
+ timestamp->Day,
+ timestamp->Hours,
+ timestamp->Minutes,
+ timestamp->Seconds);
+}
+
+//Converts a single timestamp string to an EFI timestamp.
+void string_to_timestamp(EFI_ERROR_TIME_STAMP* out, const char* timestamp)
+{
+ sscanf(timestamp, "%02d%02d-%02d-%02dT%02d:%02d:%02d.000",
+ &out->Century,
+ &out->Year,
+ &out->Month,
+ &out->Day,
+ &out->Hours,
+ &out->Minutes,
+ &out->Seconds);
+}
+
//Helper function to convert an EDK EFI GUID into a string for intermediate use.
void guid_to_string(char* out, EFI_GUID* guid)
{
@@ -159,6 +206,23 @@
guid->Data4[7]);
}
+//Helper function to convert a string into an EDK EFI GUID.
+void string_to_guid(EFI_GUID* out, const char* guid)
+{
+ sscanf(guid, "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
+ &out->Data1,
+ &out->Data2,
+ &out->Data3,
+ out->Data4,
+ out->Data4 + 1,
+ out->Data4 + 2,
+ out->Data4 + 3,
+ out->Data4 + 4,
+ out->Data4 + 5,
+ out->Data4 + 6,
+ out->Data4 + 7);
+}
+
//Returns one if two EFI GUIDs are equal, zero otherwise.
int guid_equal(EFI_GUID* a, EFI_GUID* b)
{
diff --git a/cper-utils.h b/cper-utils.h
index a0d0af9..2ef9001 100644
--- a/cper-utils.h
+++ b/cper-utils.h
@@ -9,11 +9,16 @@
json_object* uniform_struct64_to_ir(UINT64* start, int len, const char* names[]);
json_object* integer_to_readable_pair(UINT64 value, int len, int keys[], const char* values[], const char* default_value);
json_object* integer_to_readable_pair_with_desc(int value, int len, int keys[], const char* values[], const char* descriptions[], const char* default_value);
+UINT64 readable_pair_to_integer(json_object* pair);
json_object* bitfield_to_ir(UINT64 bitfield, int num_fields, const char* names[]);
+UINT64 ir_to_bitfield(json_object* ir, int num_fields, const char* names[]);
json_object* uint64_array_to_ir_array(UINT64* array, int len);
json_object* revision_to_ir(UINT16 revision);
const char* severity_to_string(UINT8 severity);
+void timestamp_to_string(char* out, EFI_ERROR_TIME_STAMP* timestamp);
+void string_to_timestamp(EFI_ERROR_TIME_STAMP* out, const char* timestamp);
void guid_to_string(char* out, EFI_GUID* guid);
+void string_to_guid(EFI_GUID* out, const char* guid);
int guid_equal(EFI_GUID* a, EFI_GUID* b);
int bcd_to_int(UINT8 bcd);
diff --git a/ir-parse.c b/ir-parse.c
index fa2a326..8eb75db 100644
--- a/ir-parse.c
+++ b/ir-parse.c
@@ -6,10 +6,89 @@
#include <stdio.h>
#include "json.h"
+#include "edk/Cper.h"
#include "cper-parse.h"
+#include "cper-utils.h"
+
+//Private pre-declarations.
+void ir_header_to_cper(json_object* header_ir, EFI_COMMON_ERROR_RECORD_HEADER* header);
//Converts the given JSON IR CPER representation into CPER binary format, piped to the provided file stream.
+//This function performs no validation of the IR against the CPER-JSON specification. For this, call
+//validate_schema() from json-schema.h before attempting to call this function.
void ir_to_cper(json_object* ir, FILE* out)
{
+ //Create the CPER header.
+ EFI_COMMON_ERROR_RECORD_HEADER* header = (EFI_COMMON_ERROR_RECORD_HEADER*)calloc(1, sizeof(EFI_COMMON_ERROR_RECORD_HEADER));
+ ir_header_to_cper(json_object_object_get(ir, "header"), header);
+ fwrite(header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1, out);
+
//...
+
+ //Free all resources.
+ fflush(out);
+ free(header);
+}
+
+//Converts a CPER-JSON IR header to a CPER header structure.
+void ir_header_to_cper(json_object* header_ir, EFI_COMMON_ERROR_RECORD_HEADER* header)
+{
+ header->SignatureStart = 0x52455043; //CPER
+ printf("beginning write.\n");
+
+ //Revision.
+ json_object* revision = json_object_object_get(header_ir, "revision");
+ int minor = json_object_get_int(json_object_object_get(revision, "minor"));
+ int major = json_object_get_int(json_object_object_get(revision, "major"));
+ header->Revision = minor + (major << 8);
+
+ header->SignatureEnd = 0xFFFFFFFF;
+
+ //Section count.
+ int section_count = json_object_get_int(json_object_object_get(header_ir, "sectionCount"));
+ header->SectionCount = (UINT16)section_count;
+
+ //Error severity.
+ json_object* severity = json_object_object_get(header_ir, "severity");
+ header->ErrorSeverity = (UINT32)json_object_get_uint64(json_object_object_get(severity, "code"));
+
+ //Validation bits.
+ header->ValidationBits = ir_to_bitfield(json_object_object_get(header_ir, "validationBits"),
+ 3, CPER_HEADER_VALID_BITFIELD_NAMES);
+
+ //Record length.
+ header->RecordLength = (UINT32)json_object_get_uint64(json_object_object_get(header_ir, "recordLength"));
+
+ //Timestamp, if present.
+ printf("timestamp write.\n");
+ json_object* timestamp = json_object_object_get(header_ir, "timestamp");
+ if (timestamp != NULL)
+ {
+ string_to_timestamp(&header->TimeStamp, json_object_get_string(timestamp));
+ header->TimeStamp.Flag = json_object_get_boolean(json_object_object_get(header_ir, "timestampIsPrecise"));
+ }
+
+ //Various GUIDs.
+ printf("guid write.\n");
+ json_object* platform_id = json_object_object_get(header_ir, "platformID");
+ json_object* partition_id = json_object_object_get(header_ir, "partitionID");
+ if (platform_id != NULL)
+ string_to_guid(&header->PlatformID, json_object_get_string(platform_id));
+ if (partition_id != NULL)
+ string_to_guid(&header->PartitionID, json_object_get_string(partition_id));
+ string_to_guid(&header->CreatorID, json_object_get_string(json_object_object_get(header_ir, "creatorID")));
+
+ //Notification type.
+ printf("notif type write.\n");
+ json_object* notification_type = json_object_object_get(header_ir, "notificationType");
+ string_to_guid(&header->NotificationType, json_object_get_string(json_object_object_get(notification_type, "guid")));
+
+ //Record ID, persistence info.
+ header->RecordID = json_object_get_uint64(json_object_object_get(header_ir, "recordID"));
+ header->PersistenceInfo = json_object_get_uint64(json_object_object_get(header_ir, "persistenceInfo"));
+
+ //Flags.
+ printf("flag write.\n");
+ json_object* flags = json_object_object_get(header_ir, "flags");
+ header->Flags = (UINT32)json_object_get_uint64(json_object_object_get(flags, "value"));
}
\ No newline at end of file
diff --git a/specification/document/cper-json-specification.tex b/specification/document/cper-json-specification.tex
index a1591b5..bafc1b6 100644
--- a/specification/document/cper-json-specification.tex
+++ b/specification/document/cper-json-specification.tex
@@ -2007,4 +2007,12 @@
slotNumber & uint64 & The slot number of the CXL component.\\
\jsontableend{CXL Component Device ID structure field table.}
+% Undefined error section.
+\section{Undefined Error Section}
+\label{section:undefinederrorsection}
+This section describes the JSON format for a single undefined CPER section. This structure is used for all CPER sections that have \texttt{errorType} GUIDs which are not defined in UEFI Appendix N.
+\jsontable{table:ccixpererrorsection}
+data & string & A base64-encoded binary dump of the undefined CPER section.\\
+\jsontableend{Undefined Error structure field table.}
+
\end{document}
\ No newline at end of file
diff --git a/testing/cper-test.c b/testing/cper-test.c
index d5e8f5b..15b77be 100644
--- a/testing/cper-test.c
+++ b/testing/cper-test.c
@@ -3,13 +3,45 @@
#include "json.h"
#include "../json-schema.h"
-int main(int argc, char* argv[]) {
+void test_ir_to_cper(int argc, char* argv[]);
+void test_cper_to_ir(int argc, char* argv[]);
+int main(int argc, char* argv[])
+{
+ test_ir_to_cper(argc, argv);
+ return 0;
+}
+
+void test_ir_to_cper(int argc, char* argv[])
+{
+ //Read JSON IR from file.
+ json_object* ir = json_object_from_file(argv[1]);
+ if (ir == NULL)
+ {
+ printf("Could not read IR JSON, import returned null.");
+ return;
+ }
+
+ //Open a read for the output file.
+ FILE* cper_file = fopen(argv[2], "w");
+ if (cper_file == NULL) {
+ printf("Could not open output file, file handle returned null.");
+ return;
+ }
+
+ //Run the converter.
+ ir_to_cper(ir, cper_file);
+ fclose(cper_file);
+ printf("Conversion finished!\n");
+}
+
+void test_cper_to_ir(int argc, char* argv[])
+{
//Get a handle for the log file.
FILE* cper_file = fopen(argv[1], "r");
if (cper_file == NULL) {
printf("Could not open CPER record, file handle returned null.");
- return -1;
+ return;
}
json_object* ir = cper_to_ir(cper_file);