Remove validation bits

Discard invalid properties from json decode. JSON output should only
contain valid properties. This saves time in preventing post
processing of output for valid fields.

Ensure round trip validity with validation bits removed and required
properties populated.

Fix bugs in json decode.

Overhaul unit tests to use valijson. Add tests with static examples
to validate against schema. Use and nlohmann for better schema
validation over intrinsic libcper validation.

Example json output before:
{
  "ValidationBits": {
    "LevelValid": false,
    "CorrectedValid": true
  },
  "Level": 1,
  "Corrected": true
}

After:
{
  "Corrected": true
}

Change-Id: I188bdc2827a57d938c22a431238fadfcdc939ab8
Signed-off-by: Aushim Nagarkatti <anagarkatti@nvidia.com>
diff --git a/sections/cper-section-cxl-protocol.c b/sections/cper-section-cxl-protocol.c
index 141f26b..151943f 100644
--- a/sections/cper-section-cxl-protocol.c
+++ b/sections/cper-section-cxl-protocol.c
@@ -17,19 +17,19 @@
 	EFI_CXL_PROTOCOL_ERROR_DATA *cxl_protocol_error =
 		(EFI_CXL_PROTOCOL_ERROR_DATA *)section;
 	json_object *section_ir = json_object_new_object();
-
-	//Validation bits.
-	json_object *validation =
-		bitfield_to_ir(cxl_protocol_error->ValidBits, 7,
-			       CXL_PROTOCOL_ERROR_VALID_BITFIELD_NAMES);
-	json_object_object_add(section_ir, "validationBits", validation);
+	ValidationTypes ui64Type = {
+		UINT_64T, .value.ui64 = cxl_protocol_error->ValidBits
+	};
 
 	//Type of detecting agent.
-	json_object *agent_type = integer_to_readable_pair(
-		cxl_protocol_error->CxlAgentType, 2,
-		CXL_PROTOCOL_ERROR_AGENT_TYPES_KEYS,
-		CXL_PROTOCOL_ERROR_AGENT_TYPES_VALUES, "Unknown (Reserved)");
-	json_object_object_add(section_ir, "agentType", agent_type);
+	if (isvalid_prop_to_ir(&ui64Type, 0)) {
+		json_object *agent_type = integer_to_readable_pair(
+			cxl_protocol_error->CxlAgentType, 2,
+			CXL_PROTOCOL_ERROR_AGENT_TYPES_KEYS,
+			CXL_PROTOCOL_ERROR_AGENT_TYPES_VALUES,
+			"Unknown (Reserved)");
+		json_object_object_add(section_ir, "agentType", agent_type);
+	}
 
 	//CXL agent address, depending on the agent type.
 	json_object *agent_address = json_object_new_object();
@@ -65,48 +65,62 @@
 				cxl_protocol_error->CxlAgentAddress
 					.PortRcrbBaseAddress));
 	}
-	json_object_object_add(section_ir, "cxlAgentAddress", agent_address);
+	if (isvalid_prop_to_ir(&ui64Type, 1)) {
+		json_object_object_add(section_ir, "cxlAgentAddress",
+				       agent_address);
+	} else {
+		json_object_put(agent_address);
+	}
 
-	//Device ID.
 	json_object *device_id = json_object_new_object();
 	json_object_object_add(
 		device_id, "vendorID",
 		json_object_new_uint64(cxl_protocol_error->DeviceId.VendorId));
-	json_object_object_add(
-		device_id, "deviceID",
-		json_object_new_uint64(cxl_protocol_error->DeviceId.DeviceId));
-	json_object_object_add(
-		device_id, "subsystemVendorID",
-		json_object_new_uint64(
-			cxl_protocol_error->DeviceId.SubsystemVendorId));
-	json_object_object_add(
-		device_id, "subsystemDeviceID",
-		json_object_new_uint64(
-			cxl_protocol_error->DeviceId.SubsystemDeviceId));
-	json_object_object_add(
-		device_id, "classCode",
-		json_object_new_uint64(cxl_protocol_error->DeviceId.ClassCode));
-	json_object_object_add(
-		device_id, "slotNumber",
-		json_object_new_uint64(
-			cxl_protocol_error->DeviceId.SlotNumber));
-	json_object_object_add(section_ir, "deviceID", device_id);
+
+	//Device ID.
+	if (isvalid_prop_to_ir(&ui64Type, 2)) {
+		json_object_object_add(
+			device_id, "deviceID",
+			json_object_new_uint64(
+				cxl_protocol_error->DeviceId.DeviceId));
+		json_object_object_add(
+			device_id, "subsystemVendorID",
+			json_object_new_uint64(
+				cxl_protocol_error->DeviceId.SubsystemVendorId));
+		json_object_object_add(
+			device_id, "subsystemDeviceID",
+			json_object_new_uint64(
+				cxl_protocol_error->DeviceId.SubsystemDeviceId));
+		json_object_object_add(
+			device_id, "classCode",
+			json_object_new_uint64(
+				cxl_protocol_error->DeviceId.ClassCode));
+		json_object_object_add(
+			device_id, "slotNumber",
+			json_object_new_uint64(
+				cxl_protocol_error->DeviceId.SlotNumber));
+		json_object_object_add(section_ir, "deviceID", device_id);
+	}
 
 	char *encoded;
-	//Device serial & capability structure (if CXL 1.1 device).
-	if (cxl_protocol_error->CxlAgentType ==
-	    CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
-		json_object_object_add(
-			section_ir, "deviceSerial",
-			json_object_new_uint64(
-				cxl_protocol_error->DeviceSerial));
 
-		//The PCIe capability structure provided here could either be PCIe 1.1 Capability Structure
-		//(36-byte, padded to 60 bytes) or PCIe 2.0 Capability Structure (60-byte). There does not seem
-		//to be a way to differentiate these, so this is left as a b64 dump.
+	if (isvalid_prop_to_ir(&ui64Type, 3)) {
+		//Device serial & capability structure (if CXL 1.1 device).
+		if (cxl_protocol_error->CxlAgentType ==
+		    CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
+			json_object_object_add(
+				section_ir, "deviceSerial",
+				json_object_new_uint64(
+					cxl_protocol_error->DeviceSerial));
+		}
+	}
 
-		int32_t encoded_len = 0;
+	int32_t encoded_len = 0;
 
+	//The PCIe capability structure provided here could either be PCIe 1.1 Capability Structure
+	//(36-byte, padded to 60 bytes) or PCIe 2.0 Capability Structure (60-byte). There does not seem
+	//to be a way to differentiate these, so this is left as a b64 dump.
+	if (isvalid_prop_to_ir(&ui64Type, 4)) {
 		encoded = base64_encode(
 			(UINT8 *)cxl_protocol_error->CapabilityStructure.PcieCap,
 			60, &encoded_len);
@@ -120,50 +134,57 @@
 		free(encoded);
 	}
 
-	//CXL DVSEC & error log length.
-	json_object_object_add(
-		section_ir, "dvsecLength",
-		json_object_new_int(cxl_protocol_error->CxlDvsecLength));
-	json_object_object_add(
-		section_ir, "errorLogLength",
-		json_object_new_int(cxl_protocol_error->CxlErrorLogLength));
-
-	//CXL DVSEC
-	//For CXL 1.1 devices, this is the "CXL DVSEC For Flex Bus Device" structure as in CXL 1.1 spec.
-	//For CXL 1.1 host downstream ports, this is the "CXL DVSEC For Flex Bus Port" structure as in CXL 1.1 spec.
 	const char *cur_pos = (const char *)(cxl_protocol_error + 1);
-	int32_t encoded_len = 0;
 
-	encoded = base64_encode((UINT8 *)cur_pos,
-				cxl_protocol_error->CxlDvsecLength,
-				&encoded_len);
-	if (encoded == NULL) {
-		return NULL;
+	if (isvalid_prop_to_ir(&ui64Type, 5)) {
+		//CXL DVSEC & error log length.
+		json_object_object_add(
+			section_ir, "dvsecLength",
+			json_object_new_int(
+				cxl_protocol_error->CxlDvsecLength));
+		//CXL DVSEC
+		//For CXL 1.1 devices, this is the "CXL DVSEC For Flex Bus Device" structure as in CXL 1.1 spec.
+		//For CXL 1.1 host downstream ports, this is the "CXL DVSEC For Flex Bus Port" structure as in CXL 1.1 spec.
+		int32_t encoded_len = 0;
+
+		encoded = base64_encode((UINT8 *)cur_pos,
+					cxl_protocol_error->CxlDvsecLength,
+					&encoded_len);
+		if (encoded == NULL) {
+			return NULL;
+		}
+		json_object_object_add(section_ir, "cxlDVSEC",
+				       json_object_new_string_len(encoded,
+								  encoded_len));
+
+		free(encoded);
 	}
-	json_object_object_add(section_ir, "cxlDVSEC",
-			       json_object_new_string_len(encoded,
-							  encoded_len));
-
-	free(encoded);
 
 	cur_pos += cxl_protocol_error->CxlDvsecLength;
 
-	//CXL Error Log
-	//This is the "CXL RAS Capability Structure" as in CXL 1.1 spec.
+	if (isvalid_prop_to_ir(&ui64Type, 6)) {
+		json_object_object_add(
+			section_ir, "errorLogLength",
+			json_object_new_int(
+				cxl_protocol_error->CxlErrorLogLength));
 
-	encoded_len = 0;
-	encoded = base64_encode((UINT8 *)cur_pos,
-				cxl_protocol_error->CxlErrorLogLength,
-				&encoded_len);
+		//CXL Error Log
+		//This is the "CXL RAS Capability Structure" as in CXL 1.1 spec.
 
-	if (encoded == NULL) {
-		printf("Failed to allocate encode output buffer. \n");
-		return NULL;
+		encoded_len = 0;
+		encoded = base64_encode((UINT8 *)cur_pos,
+					cxl_protocol_error->CxlErrorLogLength,
+					&encoded_len);
+
+		if (encoded == NULL) {
+			printf("Failed to allocate encode output buffer. \n");
+			return NULL;
+		}
+		json_object_object_add(section_ir, "cxlErrorLog",
+				       json_object_new_string_len(encoded,
+								  encoded_len));
+		free(encoded);
 	}
-	json_object_object_add(section_ir, "cxlErrorLog",
-			       json_object_new_string_len(encoded,
-							  encoded_len));
-	free(encoded);
 
 	return section_ir;
 }
@@ -174,80 +195,99 @@
 	EFI_CXL_PROTOCOL_ERROR_DATA *section_cper =
 		(EFI_CXL_PROTOCOL_ERROR_DATA *)calloc(
 			1, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA));
+	struct json_object *obj = NULL;
 
 	//Validation bits.
-	section_cper->ValidBits = ir_to_bitfield(
-		json_object_object_get(section, "validationBits"), 7,
-		CXL_PROTOCOL_ERROR_VALID_BITFIELD_NAMES);
+	ValidationTypes ui64Type = { UINT_64T, .value.ui64 = 0 };
 
 	//Detecting agent type.
-	section_cper->CxlAgentType = readable_pair_to_integer(
-		json_object_object_get(section, "agentType"));
+	if (json_object_object_get_ex(section, "agentType", &obj)) {
+		section_cper->CxlAgentType = readable_pair_to_integer(obj);
+		add_to_valid_bitfield(&ui64Type, 0);
+	}
 
 	//Based on the agent type, set the address.
-	json_object *address =
-		json_object_object_get(section, "cxlAgentAddress");
-	if (section_cper->CxlAgentType == CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
-		//Address is split by function, device, bus & segment.
-		UINT64 function = json_object_get_uint64(
-			json_object_object_get(address, "functionNumber"));
-		UINT64 device = json_object_get_uint64(
-			json_object_object_get(address, "deviceNumber"));
-		UINT64 bus = json_object_get_uint64(
-			json_object_object_get(address, "busNumber"));
-		UINT64 segment = json_object_get_uint64(
-			json_object_object_get(address, "segmentNumber"));
-		section_cper->CxlAgentAddress.DeviceAddress.FunctionNumber =
-			function;
-		section_cper->CxlAgentAddress.DeviceAddress.DeviceNumber =
-			device;
-		section_cper->CxlAgentAddress.DeviceAddress.BusNumber = bus;
-		section_cper->CxlAgentAddress.DeviceAddress.SegmentNumber =
-			segment;
-	} else if (section_cper->CxlAgentType ==
-		   CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) {
-		//Plain RCRB base address.
-		section_cper->CxlAgentAddress.PortRcrbBaseAddress =
-			json_object_get_uint64(
-				json_object_object_get(address, "value"));
+	if (json_object_object_get_ex(section, "cxlAgentAddress", &obj)) {
+		json_object *address = obj;
+		if (section_cper->CxlAgentType ==
+		    CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
+			//Address is split by function, device, bus & segment.
+			UINT64 function = json_object_get_uint64(
+				json_object_object_get(address,
+						       "functionNumber"));
+			UINT64 device = json_object_get_uint64(
+				json_object_object_get(address,
+						       "deviceNumber"));
+			UINT64 bus = json_object_get_uint64(
+				json_object_object_get(address, "busNumber"));
+			UINT64 segment = json_object_get_uint64(
+				json_object_object_get(address,
+						       "segmentNumber"));
+			section_cper->CxlAgentAddress.DeviceAddress
+				.FunctionNumber = function;
+			section_cper->CxlAgentAddress.DeviceAddress
+				.DeviceNumber = device;
+			section_cper->CxlAgentAddress.DeviceAddress.BusNumber =
+				bus;
+			section_cper->CxlAgentAddress.DeviceAddress
+				.SegmentNumber = segment;
+		} else if (section_cper->CxlAgentType ==
+			   CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) {
+			//Plain RCRB base address.
+			section_cper->CxlAgentAddress.PortRcrbBaseAddress =
+				json_object_get_uint64(json_object_object_get(
+					address, "value"));
+		}
+		add_to_valid_bitfield(&ui64Type, 1);
 	}
 
 	//Device ID information.
-	json_object *device_id = json_object_object_get(section, "deviceID");
-	section_cper->DeviceId.VendorId = json_object_get_uint64(
-		json_object_object_get(device_id, "vendorID"));
-	section_cper->DeviceId.DeviceId = json_object_get_uint64(
-		json_object_object_get(device_id, "deviceID"));
-	section_cper->DeviceId.SubsystemVendorId = json_object_get_uint64(
-		json_object_object_get(device_id, "subsystemVendorID"));
-	section_cper->DeviceId.SubsystemDeviceId = json_object_get_uint64(
-		json_object_object_get(device_id, "subsystemDeviceID"));
-	section_cper->DeviceId.ClassCode = json_object_get_uint64(
-		json_object_object_get(device_id, "classCode"));
-	section_cper->DeviceId.SlotNumber = json_object_get_uint64(
-		json_object_object_get(device_id, "slotNumber"));
+	if (json_object_object_get_ex(section, "deviceID", &obj)) {
+		json_object *device_id = obj;
+		section_cper->DeviceId.VendorId = json_object_get_uint64(
+			json_object_object_get(device_id, "vendorID"));
+		section_cper->DeviceId.DeviceId = json_object_get_uint64(
+			json_object_object_get(device_id, "deviceID"));
+		section_cper->DeviceId.SubsystemVendorId =
+			json_object_get_uint64(json_object_object_get(
+				device_id, "subsystemVendorID"));
+		section_cper->DeviceId.SubsystemDeviceId =
+			json_object_get_uint64(json_object_object_get(
+				device_id, "subsystemDeviceID"));
+		section_cper->DeviceId.ClassCode = json_object_get_uint64(
+			json_object_object_get(device_id, "classCode"));
+		section_cper->DeviceId.SlotNumber = json_object_get_uint64(
+			json_object_object_get(device_id, "slotNumber"));
+		add_to_valid_bitfield(&ui64Type, 2);
+	}
 
 	//If CXL 1.1 device, the serial number & PCI capability structure.
 	UINT8 *decoded;
 	if (section_cper->CxlAgentType == CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
-		section_cper->DeviceSerial = json_object_get_uint64(
-			json_object_object_get(section, "deviceSerial"));
+		if (json_object_object_get_ex(section, "deviceSerial", &obj)) {
+			section_cper->DeviceSerial =
+				json_object_get_uint64(obj);
+			add_to_valid_bitfield(&ui64Type, 3);
+		}
+		if (json_object_object_get_ex(section, "capabilityStructure",
+					      &obj)) {
+			json_object *encoded = obj;
 
-		json_object *encoded =
-			json_object_object_get(section, "capabilityStructure");
+			int32_t decoded_len = 0;
 
-		int32_t decoded_len = 0;
+			decoded = base64_decode(
+				json_object_get_string(encoded),
+				json_object_get_string_len(encoded),
+				&decoded_len);
 
-		decoded = base64_decode(json_object_get_string(encoded),
-					json_object_get_string_len(encoded),
-					&decoded_len);
-
-		if (decoded == NULL) {
-			printf("Failed to allocate decode output buffer. \n");
-		} else {
-			memcpy(section_cper->CapabilityStructure.PcieCap,
-			       decoded, decoded_len);
-			free(decoded);
+			if (decoded == NULL) {
+				printf("Failed to allocate decode output buffer. \n");
+			} else {
+				memcpy(section_cper->CapabilityStructure.PcieCap,
+				       decoded, decoded_len);
+				free(decoded);
+				add_to_valid_bitfield(&ui64Type, 4);
+			}
 		}
 	}
 
@@ -257,39 +297,54 @@
 	section_cper->CxlErrorLogLength = (UINT16)json_object_get_int(
 		json_object_object_get(section, "errorLogLength"));
 
+	json_object *encodedsrc = NULL;
+	json_object *encodederr = NULL;
+
+	//DVSEC out: write valid bits
+	if (json_object_object_get_ex(section, "cxlDVSEC", &obj)) {
+		add_to_valid_bitfield(&ui64Type, 5);
+		encodedsrc = obj;
+	}
+
+	//Error log: write valid bits
+	if (json_object_object_get_ex(section, "cxlErrorLog", &obj)) {
+		add_to_valid_bitfield(&ui64Type, 6);
+		encodederr = obj;
+	}
+	section_cper->ValidBits = ui64Type.value.ui64;
+
 	//Write header to stream.
 	fwrite(section_cper, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA), 1, out);
 	fflush(out);
 
 	//DVSEC out to stream.
-	json_object *encoded = json_object_object_get(section, "cxlDVSEC");
-
 	int32_t decoded_len = 0;
-
-	decoded = base64_decode(json_object_get_string(encoded),
-				json_object_get_string_len(encoded),
-				&decoded_len);
-	if (decoded == NULL) {
-		printf("Failed to allocate decode output buffer. \n");
-	} else {
-		fwrite(decoded, decoded_len, 1, out);
-		fflush(out);
-		free(decoded);
+	if (encodedsrc != NULL) {
+		decoded = base64_decode(json_object_get_string(encodedsrc),
+					json_object_get_string_len(encodedsrc),
+					&decoded_len);
+		if (decoded == NULL) {
+			printf("Failed to allocate decode output buffer. \n");
+		} else {
+			fwrite(decoded, decoded_len, 1, out);
+			fflush(out);
+			free(decoded);
+		}
 	}
 
 	//Error log out to stream.
-	encoded = json_object_object_get(section, "cxlErrorLog");
 	decoded_len = 0;
-
-	decoded = base64_decode(json_object_get_string(encoded),
-				json_object_get_string_len(encoded),
-				&decoded_len);
-	if (decoded == NULL) {
-		printf("Failed to allocate decode output buffer. \n");
-	} else {
-		fwrite(decoded, decoded_len, 1, out);
-		fflush(out);
-		free(decoded);
+	if (encodederr != NULL) {
+		decoded = base64_decode(json_object_get_string(encodederr),
+					json_object_get_string_len(encodederr),
+					&decoded_len);
+		if (decoded == NULL) {
+			printf("Failed to allocate decode output buffer. \n");
+		} else {
+			fwrite(decoded, decoded_len, 1, out);
+			fflush(out);
+			free(decoded);
+		}
 	}
 
 	free(section_cper);