blob: 346d6ea5e64a5dddb8739df59f9a1f6a7e6263ce [file] [log] [blame]
Lawrence Tang1b0b00e2022-07-05 10:33:10 +01001/**
Lawrence Tang2800cd82022-07-05 16:08:20 +01002 * Describes high level functions for converting an entire CPER log, and functions for parsing
3 * CPER headers and section descriptions into an intermediate JSON format.
Lawrence Tang1b0b00e2022-07-05 10:33:10 +01004 *
5 * Author: Lawrence.Tang@arm.com
6 **/
7
8#include <stdio.h>
Lawrence Tang5202bbb2022-08-12 14:54:36 +01009#include <json.h>
Lawrence Tangd7e8ca32022-07-07 10:25:53 +010010#include "b64.h"
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010011#include "edk/Cper.h"
12#include "cper-parse.h"
13#include "cper-utils.h"
Lawrence Tang580423f2022-08-24 09:37:53 +010014#include "sections/cper-section.h"
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010015
16//Private pre-definitions.
Lawrence Tange407b4c2022-07-21 13:54:01 +010017json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header);
18json_object *
19cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor);
20json_object *cper_section_to_ir(FILE *handle,
21 EFI_ERROR_SECTION_DESCRIPTOR *descriptor);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010022
23//Reads a CPER log file at the given file location, and returns an intermediate
24//JSON representation of this CPER record.
Lawrence Tange407b4c2022-07-21 13:54:01 +010025json_object *cper_to_ir(FILE *cper_file)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010026{
Lawrence Tange407b4c2022-07-21 13:54:01 +010027 //Ensure this is really a CPER log.
28 EFI_COMMON_ERROR_RECORD_HEADER header;
29 fseek(cper_file, 0, SEEK_SET);
30 if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1,
31 cper_file) != 1) {
32 printf("Invalid CPER file: Invalid length (log too short).\n");
33 return NULL;
34 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010035
Lawrence Tange407b4c2022-07-21 13:54:01 +010036 //Check if the header contains the magic bytes ("CPER").
37 if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
38 printf("Invalid CPER file: Invalid header (incorrect signature).\n");
39 return NULL;
40 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010041
Lawrence Tange407b4c2022-07-21 13:54:01 +010042 //Create the header JSON object from the read bytes.
43 json_object *header_ir = cper_header_to_ir(&header);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010044
Lawrence Tange407b4c2022-07-21 13:54:01 +010045 //Read the appropriate number of section descriptors & sections, and convert them into IR format.
46 json_object *section_descriptors_ir = json_object_new_array();
47 json_object *sections_ir = json_object_new_array();
48 for (int i = 0; i < header.SectionCount; i++) {
49 //Create the section descriptor.
50 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
51 if (fread(&section_descriptor,
52 sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
53 cper_file) != 1) {
54 printf("Invalid number of section headers: Header states %d sections, could not read section %d.\n",
55 header.SectionCount, i + 1);
56 return NULL;
57 }
58 json_object_array_add(
59 section_descriptors_ir,
60 cper_section_descriptor_to_ir(&section_descriptor));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010061
Lawrence Tange407b4c2022-07-21 13:54:01 +010062 //Read the section itself.
63 json_object_array_add(sections_ir,
64 cper_section_to_ir(cper_file,
65 &section_descriptor));
66 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010067
Lawrence Tange407b4c2022-07-21 13:54:01 +010068 //Add the header, section descriptors, and sections to a parent object.
69 json_object *parent = json_object_new_object();
70 json_object_object_add(parent, "header", header_ir);
71 json_object_object_add(parent, "sectionDescriptors",
72 section_descriptors_ir);
73 json_object_object_add(parent, "sections", sections_ir);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010074
Lawrence Tange407b4c2022-07-21 13:54:01 +010075 return parent;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010076}
77
78//Converts a parsed CPER record header into intermediate JSON object format.
Lawrence Tange407b4c2022-07-21 13:54:01 +010079json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010080{
Lawrence Tange407b4c2022-07-21 13:54:01 +010081 json_object *header_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010082
Lawrence Tange407b4c2022-07-21 13:54:01 +010083 //Revision/version information.
84 json_object_object_add(header_ir, "revision",
85 revision_to_ir(header->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010086
Lawrence Tange407b4c2022-07-21 13:54:01 +010087 //Section count.
88 json_object_object_add(header_ir, "sectionCount",
89 json_object_new_int(header->SectionCount));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010090
Lawrence Tange407b4c2022-07-21 13:54:01 +010091 //Error severity (with interpreted string version).
92 json_object *error_severity = json_object_new_object();
93 json_object_object_add(error_severity, "code",
94 json_object_new_uint64(header->ErrorSeverity));
95 json_object_object_add(error_severity, "name",
96 json_object_new_string(severity_to_string(
97 header->ErrorSeverity)));
98 json_object_object_add(header_ir, "severity", error_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010099
Lawrence Tange407b4c2022-07-21 13:54:01 +0100100 //The validation bits for each section.
101 json_object *validation_bits = bitfield_to_ir(
102 header->ValidationBits, 3, CPER_HEADER_VALID_BITFIELD_NAMES);
103 json_object_object_add(header_ir, "validationBits", validation_bits);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100104
Lawrence Tange407b4c2022-07-21 13:54:01 +0100105 //Total length of the record (including headers) in bytes.
106 json_object_object_add(header_ir, "recordLength",
107 json_object_new_uint64(header->RecordLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100108
Lawrence Tange407b4c2022-07-21 13:54:01 +0100109 //If a timestamp exists according to validation bits, then add it.
110 if (header->ValidationBits & 0b10) {
111 char timestamp_string[TIMESTAMP_LENGTH];
112 timestamp_to_string(timestamp_string, &header->TimeStamp);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100113
Lawrence Tange407b4c2022-07-21 13:54:01 +0100114 json_object_object_add(
115 header_ir, "timestamp",
116 json_object_new_string(timestamp_string));
117 json_object_object_add(
118 header_ir, "timestampIsPrecise",
119 json_object_new_boolean(header->TimeStamp.Flag));
120 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100121
Lawrence Tange407b4c2022-07-21 13:54:01 +0100122 //If a platform ID exists according to the validation bits, then add it.
123 if (header->ValidationBits & 0b1) {
124 char platform_string[GUID_STRING_LENGTH];
125 guid_to_string(platform_string, &header->PlatformID);
126 json_object_object_add(header_ir, "platformID",
127 json_object_new_string(platform_string));
128 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100129
Lawrence Tange407b4c2022-07-21 13:54:01 +0100130 //If a partition ID exists according to the validation bits, then add it.
131 if (header->ValidationBits & 0b100) {
132 char partition_string[GUID_STRING_LENGTH];
133 guid_to_string(partition_string, &header->PartitionID);
134 json_object_object_add(
135 header_ir, "partitionID",
136 json_object_new_string(partition_string));
137 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100138
Lawrence Tange407b4c2022-07-21 13:54:01 +0100139 //Creator ID of the header.
140 char creator_string[GUID_STRING_LENGTH];
141 guid_to_string(creator_string, &header->CreatorID);
142 json_object_object_add(header_ir, "creatorID",
143 json_object_new_string(creator_string));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100144
Lawrence Tange407b4c2022-07-21 13:54:01 +0100145 //Notification type for the header. Some defined types are available.
146 json_object *notification_type = json_object_new_object();
147 char notification_type_string[GUID_STRING_LENGTH];
148 guid_to_string(notification_type_string, &header->NotificationType);
149 json_object_object_add(
150 notification_type, "guid",
151 json_object_new_string(notification_type_string));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100152
Lawrence Tange407b4c2022-07-21 13:54:01 +0100153 //Add the human readable notification type if possible.
154 char *notification_type_readable = "Unknown";
155 if (guid_equal(&header->NotificationType,
156 &gEfiEventNotificationTypeCmcGuid))
157 notification_type_readable = "CMC";
158 else if (guid_equal(&header->NotificationType,
159 &gEfiEventNotificationTypeCpeGuid))
160 notification_type_readable = "CPE";
161 else if (guid_equal(&header->NotificationType,
162 &gEfiEventNotificationTypeMceGuid))
163 notification_type_readable = "MCE";
164 else if (guid_equal(&header->NotificationType,
165 &gEfiEventNotificationTypePcieGuid))
166 notification_type_readable = "PCIe";
167 else if (guid_equal(&header->NotificationType,
168 &gEfiEventNotificationTypeInitGuid))
169 notification_type_readable = "INIT";
170 else if (guid_equal(&header->NotificationType,
171 &gEfiEventNotificationTypeNmiGuid))
172 notification_type_readable = "NMI";
173 else if (guid_equal(&header->NotificationType,
174 &gEfiEventNotificationTypeBootGuid))
175 notification_type_readable = "Boot";
176 else if (guid_equal(&header->NotificationType,
177 &gEfiEventNotificationTypeDmarGuid))
178 notification_type_readable = "DMAr";
179 else if (guid_equal(&header->NotificationType,
180 &gEfiEventNotificationTypeSeaGuid))
181 notification_type_readable = "SEA";
182 else if (guid_equal(&header->NotificationType,
183 &gEfiEventNotificationTypeSeiGuid))
184 notification_type_readable = "SEI";
185 else if (guid_equal(&header->NotificationType,
186 &gEfiEventNotificationTypePeiGuid))
187 notification_type_readable = "PEI";
188 else if (guid_equal(&header->NotificationType,
189 &gEfiEventNotificationTypeCxlGuid))
190 notification_type_readable = "CXL Component";
191 json_object_object_add(
192 notification_type, "type",
193 json_object_new_string(notification_type_readable));
194 json_object_object_add(header_ir, "notificationType",
195 notification_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100196
Lawrence Tange407b4c2022-07-21 13:54:01 +0100197 //The record ID for this record, unique on a given system.
198 json_object_object_add(header_ir, "recordID",
199 json_object_new_uint64(header->RecordID));
200
201 //Flag for the record, and a human readable form.
202 json_object *flags = integer_to_readable_pair(
203 header->Flags,
204 sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int),
205 CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES,
206 "Unknown");
207 json_object_object_add(header_ir, "flags", flags);
208
209 //Persistence information. Outside the scope of specification, so just a uint32 here.
210 json_object_object_add(header_ir, "persistenceInfo",
211 json_object_new_uint64(header->PersistenceInfo));
212 return header_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100213}
214
215//Converts the given EFI section descriptor into JSON IR format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100216json_object *
217cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100218{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100219 json_object *section_descriptor_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100220
Lawrence Tange407b4c2022-07-21 13:54:01 +0100221 //The offset of the section from the base of the record header, length.
222 json_object_object_add(
223 section_descriptor_ir, "sectionOffset",
224 json_object_new_uint64(section_descriptor->SectionOffset));
225 json_object_object_add(
226 section_descriptor_ir, "sectionLength",
227 json_object_new_uint64(section_descriptor->SectionLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100228
Lawrence Tange407b4c2022-07-21 13:54:01 +0100229 //Revision.
230 json_object_object_add(section_descriptor_ir, "revision",
231 revision_to_ir(section_descriptor->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100232
Lawrence Tange407b4c2022-07-21 13:54:01 +0100233 //Validation bits.
234 json_object *validation_bits =
235 bitfield_to_ir(section_descriptor->SecValidMask, 2,
236 CPER_SECTION_DESCRIPTOR_VALID_BITFIELD_NAMES);
237 json_object_object_add(section_descriptor_ir, "validationBits",
238 validation_bits);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100239
Lawrence Tange407b4c2022-07-21 13:54:01 +0100240 //Flag bits.
241 json_object *flags =
242 bitfield_to_ir(section_descriptor->SectionFlags, 8,
243 CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES);
244 json_object_object_add(section_descriptor_ir, "flags", flags);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100245
Lawrence Tange407b4c2022-07-21 13:54:01 +0100246 //Section type (GUID).
247 json_object *section_type = json_object_new_object();
248 char section_type_string[GUID_STRING_LENGTH];
249 guid_to_string(section_type_string, &section_descriptor->SectionType);
250 json_object_object_add(section_type, "data",
251 json_object_new_string(section_type_string));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100252
Lawrence Tange407b4c2022-07-21 13:54:01 +0100253 //Readable section type, if possible.
Lawrence Tang580423f2022-08-24 09:37:53 +0100254 const char *section_type_readable = "Unknown";
Lawrence Tangf1f3b832022-08-24 09:42:39 +0100255 for (int i = 0; i < section_definitions_len; i++) {
256 if (guid_equal(section_definitions[i].Guid,
257 &section_descriptor->SectionType)) {
258 section_type_readable =
259 section_definitions[i].ReadableName;
Lawrence Tang580423f2022-08-24 09:37:53 +0100260 break;
261 }
262 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100263
Lawrence Tange407b4c2022-07-21 13:54:01 +0100264 json_object_object_add(section_type, "type",
265 json_object_new_string(section_type_readable));
266 json_object_object_add(section_descriptor_ir, "sectionType",
267 section_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100268
Lawrence Tange407b4c2022-07-21 13:54:01 +0100269 //If validation bits indicate it exists, add FRU ID.
270 if (section_descriptor->SecValidMask & 0b1) {
271 char fru_id_string[GUID_STRING_LENGTH];
272 guid_to_string(fru_id_string, &section_descriptor->FruId);
273 json_object_object_add(section_descriptor_ir, "fruID",
274 json_object_new_string(fru_id_string));
275 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100276
Lawrence Tange407b4c2022-07-21 13:54:01 +0100277 //If validation bits indicate it exists, add FRU text.
278 if ((section_descriptor->SecValidMask & 0b10) >> 1)
279 json_object_object_add(
280 section_descriptor_ir, "fruText",
281 json_object_new_string(section_descriptor->FruString));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100282
Lawrence Tange407b4c2022-07-21 13:54:01 +0100283 //Section severity.
284 json_object *section_severity = json_object_new_object();
285 json_object_object_add(
286 section_severity, "code",
287 json_object_new_uint64(section_descriptor->Severity));
288 json_object_object_add(section_severity, "name",
289 json_object_new_string(severity_to_string(
290 section_descriptor->Severity)));
291 json_object_object_add(section_descriptor_ir, "severity",
292 section_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100293
Lawrence Tange407b4c2022-07-21 13:54:01 +0100294 return section_descriptor_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100295}
296
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100297//Converts the section described by a single given section descriptor.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100298json_object *cper_section_to_ir(FILE *handle,
299 EFI_ERROR_SECTION_DESCRIPTOR *descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100300{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100301 //Save our current position in the stream.
302 long position = ftell(handle);
Lawrence Tangde9707f2022-07-19 10:54:31 +0100303
Lawrence Tange407b4c2022-07-21 13:54:01 +0100304 //Read section as described by the section descriptor.
305 fseek(handle, descriptor->SectionOffset, SEEK_SET);
306 void *section = malloc(descriptor->SectionLength);
307 if (fread(section, descriptor->SectionLength, 1, handle) != 1) {
308 printf("Section read failed: Could not read %d bytes from global offset %d.\n",
309 descriptor->SectionLength, descriptor->SectionOffset);
310 free(section);
311 return NULL;
312 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100313
Lawrence Tange407b4c2022-07-21 13:54:01 +0100314 //Seek back to our original position.
315 fseek(handle, position, SEEK_SET);
Lawrence Tangde9707f2022-07-19 10:54:31 +0100316
Lawrence Tange407b4c2022-07-21 13:54:01 +0100317 //Parse section to IR based on GUID.
318 json_object *result = NULL;
Lawrence Tang580423f2022-08-24 09:37:53 +0100319 int section_converted = 0;
Lawrence Tangf1f3b832022-08-24 09:42:39 +0100320 for (int i = 0; i < section_definitions_len; i++) {
321 if (guid_equal(section_definitions[i].Guid,
322 &descriptor->SectionType) &&
323 section_definitions[i].ToIR != NULL) {
324 result = section_definitions[i].ToIR(section,
325 descriptor);
Lawrence Tang580423f2022-08-24 09:37:53 +0100326 section_converted = 1;
327 break;
328 }
329 }
330
331 //Was it an unknown GUID/failed read?
332 if (!section_converted) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100333 //Output the data as formatted base64.
334 result = json_object_new_object();
335 char *encoded = b64_encode((unsigned char *)section,
336 descriptor->SectionLength);
337 json_object_object_add(result, "data",
338 json_object_new_string(encoded));
339 free(encoded);
340 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100341
Lawrence Tange407b4c2022-07-21 13:54:01 +0100342 //Free section memory, return result.
343 free(section);
344 return result;
Lawrence Tang617949e2022-08-08 14:21:42 +0100345}
346
347//Converts a single CPER section, without a header but with a section descriptor, to JSON.
348json_object *cper_single_section_to_ir(FILE *cper_section_file)
349{
350 json_object *ir = json_object_new_object();
351
352 //Read the section descriptor out.
353 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
354 if (fread(&section_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
355 cper_section_file) != 1) {
356 printf("Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n");
357 return NULL;
358 }
359
360 //Convert the section descriptor to IR.
361 json_object *section_descriptor_ir =
362 cper_section_descriptor_to_ir(&section_descriptor);
363 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
364
365 //Parse the single section.
366 json_object *section_ir =
367 cper_section_to_ir(cper_section_file, &section_descriptor);
368 json_object_object_add(ir, "section", section_ir);
369
370 return ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100371}