Improve Nvidia CPER decode

Add decoding of registers to the structure.  Note, this requires
COUNTED_BY support which is borrowed from LIBPLDM.

Also add unit-tests for NVIDIA section, and update schema to match
existing register decoding.

Change-Id: If1c9cae97de35ba6a5dad1f462d3989ec6ac6a90
Signed-off-by: Karthik Rajagopalan <krajagopalan@nvidia.com>
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/common-utils.h b/common-utils.h
index c6b9b60..6cdccb6 100644
--- a/common-utils.h
+++ b/common-utils.h
@@ -6,4 +6,14 @@
 int bcd_to_int(UINT8 bcd);
 UINT8 int_to_bcd(int value);
 
+#if defined __has_attribute
+#if __has_attribute(counted_by)
+#define LIBCPER_CC_COUNTED_BY(x) __attribute__((counted_by(x)))
+#endif
+#endif
+
+#ifndef LIBCPER_CC_COUNTED_BY
+#define LIBCPER_CC_COUNTED_BY(x)
+#endif
+
 #endif
diff --git a/edk/Cper.h b/edk/Cper.h
index f1bbdb1..5160eba 100644
--- a/edk/Cper.h
+++ b/edk/Cper.h
@@ -14,6 +14,7 @@
 #define __CPER_GUID_H__
 
 #include "BaseTypes.h"
+#include "common-utils.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -1392,6 +1393,11 @@
 /// NVIDIA Error Record Section
 ///
 typedef struct {
+	UINT64 Address;
+	UINT64 Value;
+} EFI_NVIDIA_REGISTER_DATA;
+
+typedef struct {
 	CHAR8 Signature[16];
 	UINT16 ErrorType;
 	UINT16 ErrorInstance;
@@ -1400,6 +1406,11 @@
 	UINT8 NumberRegs;
 	UINT8 Reserved;
 	UINT64 InstanceBase;
+	// Keep this at the end of this struct
+	// and allocate based on NumberRegs
+#ifndef __cplusplus
+	EFI_NVIDIA_REGISTER_DATA Register[] LIBCPER_CC_COUNTED_BY(NumberRegs);
+#endif
 } EFI_NVIDIA_ERROR_DATA;
 
 extern EFI_GUID gEfiNvidiaErrorSectionGuid;
diff --git a/generator/sections/gen-section-nvidia.c b/generator/sections/gen-section-nvidia.c
index d5103a6..0cb4cb4 100644
--- a/generator/sections/gen-section-nvidia.c
+++ b/generator/sections/gen-section-nvidia.c
@@ -4,6 +4,7 @@
  **/
 
 #include <stdlib.h>
+#include <stddef.h>
 #include <string.h>
 #include <stdio.h>
 #include "../../edk/BaseTypes.h"
@@ -26,13 +27,21 @@
 	init_random();
 
 	//Create random bytes.
-	size_t size = sizeof(EFI_NVIDIA_ERROR_DATA);
+	int numRegs = 6;
+	size_t size = offsetof(EFI_NVIDIA_ERROR_DATA, Register) +
+		      numRegs * sizeof(EFI_NVIDIA_REGISTER_DATA);
 	UINT8 *section = generate_random_bytes(size);
 
 	//Reserved byte.
 	EFI_NVIDIA_ERROR_DATA *nvidia_error = (EFI_NVIDIA_ERROR_DATA *)section;
 	nvidia_error->Reserved = 0;
 
+	//Number of Registers.
+	nvidia_error->NumberRegs = numRegs;
+
+	//Severity (0 to 3 as defined in UEFI spec).
+	nvidia_error->Severity %= 4;
+
 	//Signature.
 	int idx_random = rand() % (sizeof(signatures) / sizeof(signatures[0]));
 	strncpy(nvidia_error->Signature, signatures[idx_random],
diff --git a/sections/cper-section-nvidia.c b/sections/cper-section-nvidia.c
index e2f8aef..c5eb493 100644
--- a/sections/cper-section-nvidia.c
+++ b/sections/cper-section-nvidia.c
@@ -4,6 +4,7 @@
  **/
 
 #include <stdio.h>
+#include <stddef.h>
 #include <string.h>
 #include <json.h>
 #include "../edk/Cper.h"
@@ -16,21 +17,25 @@
 	EFI_NVIDIA_ERROR_DATA *nvidia_error = (EFI_NVIDIA_ERROR_DATA *)section;
 	json_object *section_ir = json_object_new_object();
 
-	//Signature.
 	json_object_object_add(section_ir, "signature",
 			       json_object_new_string(nvidia_error->Signature));
 
-	//Fields.
+	json_object *severity = json_object_new_object();
+	json_object_object_add(severity, "code",
+			       json_object_new_uint64(nvidia_error->Severity));
+	json_object_object_add(severity, "name",
+			       json_object_new_string(severity_to_string(
+				       nvidia_error->Severity)));
+	json_object_object_add(section_ir, "severity", severity);
+
 	json_object_object_add(section_ir, "errorType",
 			       json_object_new_int(nvidia_error->ErrorType));
 	json_object_object_add(
 		section_ir, "errorInstance",
 		json_object_new_int(nvidia_error->ErrorInstance));
-	json_object_object_add(section_ir, "severity",
-			       json_object_new_int(nvidia_error->Severity));
 	json_object_object_add(section_ir, "socket",
 			       json_object_new_int(nvidia_error->Socket));
-	json_object_object_add(section_ir, "numberRegs",
+	json_object_object_add(section_ir, "registerCount",
 			       json_object_new_int(nvidia_error->NumberRegs));
 	json_object_object_add(
 		section_ir, "instanceBase",
@@ -38,13 +43,13 @@
 
 	// Registers (Address Value pairs).
 	json_object *regarr = json_object_new_array();
-	UINT64 *regPtr = &nvidia_error->InstanceBase;
-	for (int i = 0; i < nvidia_error->NumberRegs; i++) {
+	EFI_NVIDIA_REGISTER_DATA *regPtr = nvidia_error->Register;
+	for (int i = 0; i < nvidia_error->NumberRegs; i++, regPtr++) {
 		json_object *reg = json_object_new_object();
 		json_object_object_add(reg, "address",
-				       json_object_new_uint64(*++regPtr));
+				       json_object_new_uint64(regPtr->Address));
 		json_object_object_add(reg, "value",
-				       json_object_new_uint64(*++regPtr));
+				       json_object_new_uint64(regPtr->Value));
 		json_object_array_add(regarr, reg);
 	}
 	json_object_object_add(section_ir, "registers", regarr);
@@ -58,8 +63,8 @@
 	json_object *regarr = json_object_object_get(section, "registers");
 	int numRegs = json_object_array_length(regarr);
 
-	size_t section_sz =
-		sizeof(EFI_NVIDIA_ERROR_DATA) + (numRegs * 2 * sizeof(UINT64));
+	size_t section_sz = offsetof(EFI_NVIDIA_ERROR_DATA, Register) +
+			    numRegs * sizeof(EFI_NVIDIA_REGISTER_DATA);
 	EFI_NVIDIA_ERROR_DATA *section_cper =
 		(EFI_NVIDIA_ERROR_DATA *)calloc(1, section_sz);
 
@@ -75,22 +80,23 @@
 		json_object_object_get(section, "errorType"));
 	section_cper->ErrorInstance = json_object_get_int(
 		json_object_object_get(section, "errorInstance"));
-	section_cper->Severity = json_object_get_int(
-		json_object_object_get(section, "severity"));
+	json_object *severity = json_object_object_get(section, "severity");
+	section_cper->Severity = (UINT8)json_object_get_uint64(
+		json_object_object_get(severity, "code"));
 	section_cper->Socket =
 		json_object_get_int(json_object_object_get(section, "socket"));
 	section_cper->NumberRegs = json_object_get_int(
-		json_object_object_get(section, "numberRegs"));
+		json_object_object_get(section, "registerCount"));
 	section_cper->InstanceBase = json_object_get_uint64(
 		json_object_object_get(section, "instanceBase"));
 
 	// Registers (Address Value pairs).
-	UINT64 *regPtr = &section_cper->InstanceBase;
-	for (int i = 0; i < numRegs; i++) {
+	EFI_NVIDIA_REGISTER_DATA *regPtr = section_cper->Register;
+	for (int i = 0; i < numRegs; i++, regPtr++) {
 		json_object *reg = json_object_array_get_idx(regarr, i);
-		*++regPtr = json_object_get_uint64(
+		regPtr->Address = json_object_get_uint64(
 			json_object_object_get(reg, "address"));
-		*++regPtr = json_object_get_uint64(
+		regPtr->Value = json_object_get_uint64(
 			json_object_object_get(reg, "value"));
 	}
 
diff --git a/specification/json/sections/cper-nvidia.json b/specification/json/sections/cper-nvidia.json
index 0c5f061..9512fb3 100644
--- a/specification/json/sections/cper-nvidia.json
+++ b/specification/json/sections/cper-nvidia.json
@@ -23,7 +23,17 @@
             "type": "integer"
         },
         "severity": {
-            "type": "integer"
+            "type": "object",
+            "required": ["code", "name"],
+            "properties": {
+                "code": {
+                    "type": "integer",
+                    "minimum": 0
+                },
+                "name": {
+                    "type": "string"
+                }
+            }
         },
         "socket": {
             "type": "integer"
@@ -33,6 +43,21 @@
         },
         "instanceBase": {
             "type": "integer"
+        },
+        "registers": {
+            "type": "array",
+            "items": {
+                "type": "object",
+                "required": ["address", "value"],
+                "properties": {
+                    "address": {
+                        "type": "integer"
+                    },
+                    "value": {
+                        "type": "integer"
+                    }
+                }
+            }
         }
     }
 }
diff --git a/tests/ir-tests.cpp b/tests/ir-tests.cpp
index 061f0a8..0146cac 100644
--- a/tests/ir-tests.cpp
+++ b/tests/ir-tests.cpp
@@ -293,6 +293,16 @@
 	cper_log_section_dual_binary_test("cxlcomponent-media");
 }
 
+//NVIDIA section tests.
+TEST(NVIDIASectionTests, IRValid)
+{
+	cper_log_section_dual_ir_test("nvidia");
+}
+TEST(NVIDIASectionTests, BinaryEqual)
+{
+	cper_log_section_dual_binary_test("nvidia");
+}
+
 //Unknown section tests.
 TEST(UnknownSectionTests, IRValid)
 {