blob: ccd863f8d3a3748cab7e42d3b99d950fa5dbc143 [file] [log] [blame]
Lawrence Tang1b0b00e2022-07-05 10:33:10 +01001/**
Ed Tanousfedd4572024-07-12 13:56:00 -07002 * Describes high level functions for converting an entire CPER log, and functions for parsing
Lawrence Tang2800cd82022-07-05 16:08:20 +01003 * CPER headers and section descriptions into an intermediate JSON format.
Ed Tanousfedd4572024-07-12 13:56:00 -07004 *
Lawrence Tang1b0b00e2022-07-05 10:33:10 +01005 * Author: Lawrence.Tang@arm.com
6 **/
7
Ed Tanous73498f62025-03-05 18:03:36 -08008#include <limits.h>
Lawrence Tang1b0b00e2022-07-05 10:33:10 +01009#include <stdio.h>
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -070010#include <string.h>
Lawrence Tang5202bbb2022-08-12 14:54:36 +010011#include <json.h>
Ed Tanous73498f62025-03-05 18:03:36 -080012
Thu Nguyene42fb482024-10-15 14:43:11 +000013#include <libcper/base64.h>
14#include <libcper/Cper.h>
Ed Tanous50b966f2025-03-11 09:06:19 -070015#include <libcper/log.h>
Thu Nguyene42fb482024-10-15 14:43:11 +000016#include <libcper/cper-parse.h>
17#include <libcper/cper-parse-str.h>
18#include <libcper/cper-utils.h>
19#include <libcper/sections/cper-section.h>
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010020
21//Private pre-definitions.
Lawrence Tange407b4c2022-07-21 13:54:01 +010022json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header);
23json_object *
24cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +010025
Ed Tanous73498f62025-03-05 18:03:36 -080026json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
27 EFI_ERROR_SECTION_DESCRIPTOR *descriptor);
28
29json_object *cper_buf_to_ir(const unsigned char *cper_buf, size_t size)
Ed Tanous8d47a372025-03-05 15:55:36 -080030{
Ed Tanous73498f62025-03-05 18:03:36 -080031 json_object *parent = NULL;
32 json_object *header_ir = NULL;
33 json_object *section_descriptors_ir = NULL;
34 json_object *sections_ir = NULL;
35
36 const unsigned char *pos = cper_buf;
37 unsigned int remaining = size;
38
39 if (remaining < sizeof(EFI_COMMON_ERROR_RECORD_HEADER)) {
Ed Tanous50b966f2025-03-11 09:06:19 -070040 cper_print_log(
41 "Invalid CPER file: Invalid header (incorrect signature).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080042 goto fail;
Ed Tanous8d47a372025-03-05 15:55:36 -080043 }
Ed Tanous73498f62025-03-05 18:03:36 -080044
45 EFI_COMMON_ERROR_RECORD_HEADER *header = NULL;
46 header = (EFI_COMMON_ERROR_RECORD_HEADER *)cper_buf;
47 pos += sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
48 remaining -= sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
49 if (header->SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
Ed Tanous50b966f2025-03-11 09:06:19 -070050 cper_print_log(
51 "Invalid CPER file: Invalid header (incorrect signature).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080052 goto fail;
53 }
54 if (header->SectionCount == 0) {
Ed Tanous50b966f2025-03-11 09:06:19 -070055 cper_print_log(
56 "Invalid CPER file: Invalid section count (0).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080057 goto fail;
58 }
59 if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
Ed Tanous50b966f2025-03-11 09:06:19 -070060 cper_print_log(
61 "Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080062 goto fail;
63 }
64
65 //Create the header JSON object from the read bytes.
66 parent = json_object_new_object();
67 header_ir = cper_header_to_ir(header);
68
69 json_object_object_add(parent, "header", header_ir);
70
71 //Read the appropriate number of section descriptors & sections, and convert them into IR format.
72 section_descriptors_ir = json_object_new_array();
73 sections_ir = json_object_new_array();
74 for (int i = 0; i < header->SectionCount; i++) {
75 //Create the section descriptor.
76 if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
Ed Tanous50b966f2025-03-11 09:06:19 -070077 cper_print_log(
78 "Invalid number of section headers: Header states %d sections, could not read section %d.\n",
79 header->SectionCount, i + 1);
Ed Tanous73498f62025-03-05 18:03:36 -080080 goto fail;
81 }
82
83 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
84 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)(pos);
85 pos += sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
86 remaining -= sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
87
88 if (section_descriptor->SectionOffset > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -070089 cper_print_log(
90 "Invalid section descriptor: Section offset > size.\n");
Ed Tanous73498f62025-03-05 18:03:36 -080091 goto fail;
92 }
93
94 if (section_descriptor->SectionLength <= 0) {
Ed Tanous50b966f2025-03-11 09:06:19 -070095 cper_print_log(
96 "Invalid section descriptor: Section length <= 0.\n");
Ed Tanous73498f62025-03-05 18:03:36 -080097 goto fail;
98 }
99
100 if (section_descriptor->SectionOffset >
101 UINT_MAX - section_descriptor->SectionLength) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700102 cper_print_log(
103 "Invalid section descriptor: Section offset + length would overflow.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800104 goto fail;
105 }
106
107 if (section_descriptor->SectionOffset +
108 section_descriptor->SectionLength >
109 size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700110 cper_print_log(
111 "Invalid section descriptor: Section offset + length > size.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800112 goto fail;
113 }
114
115 const unsigned char *section_begin =
116 cper_buf + section_descriptor->SectionOffset;
Ed Tanousd6b62632025-03-14 15:30:07 -0700117 json_object *section_descriptor_ir =
118 cper_section_descriptor_to_ir(section_descriptor);
119 json_object_array_add(section_descriptors_ir,
120 section_descriptor_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800121
122 //Read the section itself.
123 json_object *section_ir = cper_buf_section_to_ir(
124 section_begin, section_descriptor->SectionLength,
125 section_descriptor);
Ed Tanousd6b62632025-03-14 15:30:07 -0700126 if (section_ir == NULL) {
127 cper_print_log("Failed to parse section %d\n", i);
128 section_ir = json_object_new_object();
129 }
Ed Tanous73498f62025-03-05 18:03:36 -0800130 json_object_array_add(sections_ir, section_ir);
131 }
132
133 //Add the header, section descriptors, and sections to a parent object.
134 json_object_object_add(parent, "sectionDescriptors",
135 section_descriptors_ir);
136 json_object_object_add(parent, "sections", sections_ir);
137
138 return parent;
139
140fail:
141 json_object_put(sections_ir);
142 json_object_put(section_descriptors_ir);
143 json_object_put(parent);
Ed Tanous50b966f2025-03-11 09:06:19 -0700144 cper_print_log("Failed to parse CPER file.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800145 return NULL;
Ed Tanous8d47a372025-03-05 15:55:36 -0800146}
147
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100148//Reads a CPER log file at the given file location, and returns an intermediate
149//JSON representation of this CPER record.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100150json_object *cper_to_ir(FILE *cper_file)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100151{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100152 //Ensure this is really a CPER log.
153 EFI_COMMON_ERROR_RECORD_HEADER header;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100154 if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1,
155 cper_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700156 cper_print_log(
157 "Invalid CPER file: Invalid length (log too short).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100158 return NULL;
159 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100160
Lawrence Tange407b4c2022-07-21 13:54:01 +0100161 //Check if the header contains the magic bytes ("CPER").
162 if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700163 cper_print_log(
164 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100165 return NULL;
166 }
Ed Tanous73498f62025-03-05 18:03:36 -0800167 fseek(cper_file, -sizeof(EFI_COMMON_ERROR_RECORD_HEADER), SEEK_CUR);
168 unsigned char *cper_buf = malloc(header.RecordLength);
169 if (fread(cper_buf, header.RecordLength, 1, cper_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700170 cper_print_log("File read failed\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800171 free(cper_buf);
172 return NULL;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100173 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100174
Ed Tanous73498f62025-03-05 18:03:36 -0800175 json_object *ir = cper_buf_to_ir(cper_buf, header.RecordLength);
176 free(cper_buf);
177 return ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100178}
179
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700180char *cper_to_str_ir(FILE *cper_file)
181{
182 json_object *jobj = cper_to_ir(cper_file);
183 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
184
185 json_object_put(jobj);
186 return str;
187}
188
189char *cperbuf_to_str_ir(const unsigned char *cper, size_t size)
190{
191 FILE *cper_file = fmemopen((void *)cper, size, "r");
192
193 return cper_file ? cper_to_str_ir(cper_file) : NULL;
194}
195
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100196//Converts a parsed CPER record header into intermediate JSON object format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100197json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100198{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100199 json_object *header_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100200
Lawrence Tange407b4c2022-07-21 13:54:01 +0100201 //Revision/version information.
202 json_object_object_add(header_ir, "revision",
203 revision_to_ir(header->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100204
Lawrence Tange407b4c2022-07-21 13:54:01 +0100205 //Section count.
206 json_object_object_add(header_ir, "sectionCount",
207 json_object_new_int(header->SectionCount));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100208
Lawrence Tange407b4c2022-07-21 13:54:01 +0100209 //Error severity (with interpreted string version).
210 json_object *error_severity = json_object_new_object();
211 json_object_object_add(error_severity, "code",
212 json_object_new_uint64(header->ErrorSeverity));
213 json_object_object_add(error_severity, "name",
214 json_object_new_string(severity_to_string(
215 header->ErrorSeverity)));
216 json_object_object_add(header_ir, "severity", error_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100217
Lawrence Tange407b4c2022-07-21 13:54:01 +0100218 //Total length of the record (including headers) in bytes.
219 json_object_object_add(header_ir, "recordLength",
220 json_object_new_uint64(header->RecordLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100221
Lawrence Tange407b4c2022-07-21 13:54:01 +0100222 //If a timestamp exists according to validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800223 if (header->ValidationBits & 0x2) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100224 char timestamp_string[TIMESTAMP_LENGTH];
Ed Tanous596c59e2025-03-10 13:15:58 -0700225 if (timestamp_to_string(timestamp_string, TIMESTAMP_LENGTH,
Ed Tanousd6b62632025-03-14 15:30:07 -0700226 &header->TimeStamp) >= 0) {
227 json_object_object_add(
228 header_ir, "timestamp",
229 json_object_new_string(timestamp_string));
Ed Tanous596c59e2025-03-10 13:15:58 -0700230
Ed Tanousd6b62632025-03-14 15:30:07 -0700231 json_object_object_add(header_ir, "timestampIsPrecise",
232 json_object_new_boolean(
233 header->TimeStamp.Flag));
234 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100235 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100236
Lawrence Tange407b4c2022-07-21 13:54:01 +0100237 //If a platform ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800238 if (header->ValidationBits & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700239 add_guid(header_ir, "platformID", &header->PlatformID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100240 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100241
Lawrence Tange407b4c2022-07-21 13:54:01 +0100242 //If a partition ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800243 if (header->ValidationBits & 0x4) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700244 add_guid(header_ir, "partitionID", &header->PartitionID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100245 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100246
Lawrence Tange407b4c2022-07-21 13:54:01 +0100247 //Creator ID of the header.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700248 add_guid(header_ir, "creatorID", &header->CreatorID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100249 //Notification type for the header. Some defined types are available.
250 json_object *notification_type = json_object_new_object();
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700251 add_guid(notification_type, "guid", &header->NotificationType);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100252
Lawrence Tange407b4c2022-07-21 13:54:01 +0100253 //Add the human readable notification type if possible.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700254 const char *notification_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700255
256 EFI_GUID *guids[] = {
257 &gEfiEventNotificationTypeCmcGuid,
258 &gEfiEventNotificationTypeCpeGuid,
259 &gEfiEventNotificationTypeMceGuid,
260 &gEfiEventNotificationTypePcieGuid,
261 &gEfiEventNotificationTypeInitGuid,
262 &gEfiEventNotificationTypeNmiGuid,
263 &gEfiEventNotificationTypeBootGuid,
264 &gEfiEventNotificationTypeDmarGuid,
265 &gEfiEventNotificationTypeSeaGuid,
266 &gEfiEventNotificationTypeSeiGuid,
267 &gEfiEventNotificationTypePeiGuid,
268 &gEfiEventNotificationTypeCxlGuid,
269 };
270
271 const char *readable_names[] = {
272 "CMC", "CPE", "MCE", "PCIe", "INIT", "NMI",
273 "Boot", "DMAr", "SEA", "SEI", "PEI", "CXL Component"
274 };
275
276 int index = select_guid_from_list(&header->NotificationType, guids,
277 sizeof(guids) / sizeof(EFI_GUID *));
278 if (index < (int)(sizeof(readable_names) / sizeof(char *))) {
279 notification_type_readable = readable_names[index];
John Chungf8fc7052024-05-03 20:05:29 +0800280 }
Ed Tanous1a648562025-03-10 15:23:38 -0700281
Lawrence Tange407b4c2022-07-21 13:54:01 +0100282 json_object_object_add(
283 notification_type, "type",
284 json_object_new_string(notification_type_readable));
285 json_object_object_add(header_ir, "notificationType",
286 notification_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100287
Lawrence Tange407b4c2022-07-21 13:54:01 +0100288 //The record ID for this record, unique on a given system.
289 json_object_object_add(header_ir, "recordID",
290 json_object_new_uint64(header->RecordID));
291
292 //Flag for the record, and a human readable form.
293 json_object *flags = integer_to_readable_pair(
294 header->Flags,
295 sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int),
296 CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES,
297 "Unknown");
298 json_object_object_add(header_ir, "flags", flags);
299
300 //Persistence information. Outside the scope of specification, so just a uint32 here.
301 json_object_object_add(header_ir, "persistenceInfo",
302 json_object_new_uint64(header->PersistenceInfo));
303 return header_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100304}
305
306//Converts the given EFI section descriptor into JSON IR format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100307json_object *
308cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100309{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100310 json_object *section_descriptor_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100311
Lawrence Tange407b4c2022-07-21 13:54:01 +0100312 //The offset of the section from the base of the record header, length.
313 json_object_object_add(
314 section_descriptor_ir, "sectionOffset",
315 json_object_new_uint64(section_descriptor->SectionOffset));
316 json_object_object_add(
317 section_descriptor_ir, "sectionLength",
318 json_object_new_uint64(section_descriptor->SectionLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100319
Lawrence Tange407b4c2022-07-21 13:54:01 +0100320 //Revision.
321 json_object_object_add(section_descriptor_ir, "revision",
322 revision_to_ir(section_descriptor->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100323
Lawrence Tange407b4c2022-07-21 13:54:01 +0100324 //Flag bits.
325 json_object *flags =
326 bitfield_to_ir(section_descriptor->SectionFlags, 8,
327 CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES);
328 json_object_object_add(section_descriptor_ir, "flags", flags);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100329
Lawrence Tange407b4c2022-07-21 13:54:01 +0100330 //Section type (GUID).
331 json_object *section_type = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100332
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700333 add_guid(section_type, "data", &section_descriptor->SectionType);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100334 //Readable section type, if possible.
Lawrence Tang580423f2022-08-24 09:37:53 +0100335 const char *section_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700336
337 CPER_SECTION_DEFINITION *section =
338 select_section_by_guid(&section_descriptor->SectionType);
339 if (section != NULL) {
340 section_type_readable = section->ReadableName;
Lawrence Tang580423f2022-08-24 09:37:53 +0100341 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100342
Lawrence Tange407b4c2022-07-21 13:54:01 +0100343 json_object_object_add(section_type, "type",
344 json_object_new_string(section_type_readable));
345 json_object_object_add(section_descriptor_ir, "sectionType",
346 section_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100347
Lawrence Tange407b4c2022-07-21 13:54:01 +0100348 //If validation bits indicate it exists, add FRU ID.
John Chungf8fc7052024-05-03 20:05:29 +0800349 if (section_descriptor->SecValidMask & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700350 add_guid(section_descriptor_ir, "fruID",
351 &section_descriptor->FruId);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100352 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100353
Lawrence Tange407b4c2022-07-21 13:54:01 +0100354 //If validation bits indicate it exists, add FRU text.
John Chungf8fc7052024-05-03 20:05:29 +0800355 if ((section_descriptor->SecValidMask & 0x2) >> 1) {
Ed Tanous8121f7e2025-03-06 14:39:07 -0800356 int fru_text_len = 0;
357 for (;
358 fru_text_len < (int)sizeof(section_descriptor->FruString);
359 fru_text_len++) {
360 char c = section_descriptor->FruString[fru_text_len];
361 if (c < 0) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700362 //cper_print_log("Fru text contains non-ASCII character\n");
Ed Tanous8121f7e2025-03-06 14:39:07 -0800363 fru_text_len = -1;
364 break;
365 }
366 if (c == '\0') {
367 break;
368 }
369 }
370 if (fru_text_len >= 0) {
371 json_object_object_add(
372 section_descriptor_ir, "fruText",
373 json_object_new_string_len(
374 section_descriptor->FruString,
375 fru_text_len));
376 }
John Chungf8fc7052024-05-03 20:05:29 +0800377 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100378
Lawrence Tange407b4c2022-07-21 13:54:01 +0100379 //Section severity.
380 json_object *section_severity = json_object_new_object();
381 json_object_object_add(
382 section_severity, "code",
383 json_object_new_uint64(section_descriptor->Severity));
384 json_object_object_add(section_severity, "name",
385 json_object_new_string(severity_to_string(
386 section_descriptor->Severity)));
387 json_object_object_add(section_descriptor_ir, "severity",
388 section_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100389
Lawrence Tange407b4c2022-07-21 13:54:01 +0100390 return section_descriptor_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100391}
392
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800393json_object *read_section(const unsigned char *cper_section_buf, size_t size,
394 CPER_SECTION_DEFINITION *definition)
Ed Tanousd759a182025-03-07 15:51:45 -0800395{
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800396 if (definition->ToIR == NULL) {
397 return NULL;
398 }
399 json_object *section_ir = definition->ToIR(cper_section_buf, size);
Ed Tanousd6b62632025-03-14 15:30:07 -0700400 if (section_ir == NULL) {
401 return NULL;
402 }
Ed Tanousd759a182025-03-07 15:51:45 -0800403 json_object *result = json_object_new_object();
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800404 json_object_object_add(result, definition->ShortName, section_ir);
Ed Tanousd759a182025-03-07 15:51:45 -0800405 return result;
406}
407
Ed Tanous1a648562025-03-10 15:23:38 -0700408CPER_SECTION_DEFINITION *select_section_by_guid(EFI_GUID *guid)
409{
410 size_t i = 0;
411 for (; i < section_definitions_len; i++) {
412 if (guid_equal(guid, section_definitions[i].Guid)) {
413 break;
414 }
415 }
416 // It's unlikely fuzzing can reliably come up with a correct guid, given how
417 // much entropy there is. If we're in fuzzing mode, and if we haven't found
418 // a match, try to force a match so we get some coverage. Note, we still
419 // want coverage of the section failed to convert code, so treat index ==
420 // size as section failed to convert.
421#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
422 if (i == section_definitions_len) {
423 i = guid->Data1 % (section_definitions_len + 1);
424 }
425#endif
426 if (i < section_definitions_len) {
427 return &section_definitions[i];
428 }
429
430 return NULL;
431}
432
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100433//Converts the section described by a single given section descriptor.
Ed Tanous73498f62025-03-05 18:03:36 -0800434json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
435 EFI_ERROR_SECTION_DESCRIPTOR *descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100436{
Ed Tanous73498f62025-03-05 18:03:36 -0800437 if (descriptor->SectionLength > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700438 cper_print_log(
439 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100440 return NULL;
441 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100442
Lawrence Tange407b4c2022-07-21 13:54:01 +0100443 //Parse section to IR based on GUID.
444 json_object *result = NULL;
Ed Tanousb07061a2024-09-22 10:33:29 -0700445 json_object *section_ir = NULL;
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800446
Ed Tanous1a648562025-03-10 15:23:38 -0700447 CPER_SECTION_DEFINITION *section =
448 select_section_by_guid(&descriptor->SectionType);
Ed Tanousd6b62632025-03-14 15:30:07 -0700449 if (section == NULL) {
450 cper_print_log("Unknown section type guid\n");
451 } else {
Ed Tanous1a648562025-03-10 15:23:38 -0700452 result = read_section(cper_section_buf, size, section);
Ed Tanousd759a182025-03-07 15:51:45 -0800453 }
Ed Tanousb07061a2024-09-22 10:33:29 -0700454
Lawrence Tang580423f2022-08-24 09:37:53 +0100455 //Was it an unknown GUID/failed read?
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800456 if (result == NULL) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100457 //Output the data as formatted base64.
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700458 int32_t encoded_len = 0;
Ed Tanous73498f62025-03-05 18:03:36 -0800459 char *encoded = base64_encode(cper_section_buf,
460 descriptor->SectionLength,
461 &encoded_len);
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700462 if (encoded == NULL) {
Ed Tanousd6b62632025-03-14 15:30:07 -0700463 cper_print_log(
464 "Failed to allocate encode output buffer. \n");
John Chungf8fc7052024-05-03 20:05:29 +0800465 } else {
Ed Tanousb07061a2024-09-22 10:33:29 -0700466 section_ir = json_object_new_object();
467 json_object_object_add(section_ir, "data",
John Chungf8fc7052024-05-03 20:05:29 +0800468 json_object_new_string_len(
469 encoded, encoded_len));
470 free(encoded);
Ed Tanousb07061a2024-09-22 10:33:29 -0700471
472 result = json_object_new_object();
473 json_object_object_add(result, "Unknown", section_ir);
John Chungf8fc7052024-05-03 20:05:29 +0800474 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100475 }
Ed Tanousd6b62632025-03-14 15:30:07 -0700476 if (result == NULL) {
477 cper_print_log("RETURNING NULL!! !!\n");
478 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100479 return result;
Lawrence Tang617949e2022-08-08 14:21:42 +0100480}
481
Ed Tanous73498f62025-03-05 18:03:36 -0800482json_object *cper_buf_single_section_to_ir(const unsigned char *cper_buf,
483 size_t size)
Ed Tanous8d47a372025-03-05 15:55:36 -0800484{
Ed Tanous8121f7e2025-03-06 14:39:07 -0800485 const unsigned char *cper_end;
486 const unsigned char *section_begin;
487 json_object *ir;
488
489 cper_end = cper_buf + size;
Ed Tanous73498f62025-03-05 18:03:36 -0800490
491 //Read the section descriptor out.
492 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
493 if (sizeof(EFI_ERROR_SECTION_DESCRIPTOR) > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700494 cper_print_log(
495 "Size of cper buffer was too small to read section descriptor %zu\n",
496 size);
Ed Tanous8d47a372025-03-05 15:55:36 -0800497 return NULL;
498 }
Ed Tanous8121f7e2025-03-06 14:39:07 -0800499
500 ir = json_object_new_object();
Ed Tanous73498f62025-03-05 18:03:36 -0800501 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)cper_buf;
502 //Convert the section descriptor to IR.
503 json_object *section_descriptor_ir =
504 cper_section_descriptor_to_ir(section_descriptor);
505 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800506 section_begin = cper_buf + section_descriptor->SectionOffset;
Ed Tanous73498f62025-03-05 18:03:36 -0800507
Ed Tanous8121f7e2025-03-06 14:39:07 -0800508 if (section_begin + section_descriptor->SectionLength >= cper_end) {
509 json_object_put(ir);
Ed Tanous50b966f2025-03-11 09:06:19 -0700510 //cper_print_log("Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800511 return NULL;
512 }
513
514 const unsigned char *section =
515 cper_buf + section_descriptor->SectionOffset;
516
517 //Parse the single section.
518 json_object *section_ir = cper_buf_section_to_ir(
519 section, section_descriptor->SectionLength, section_descriptor);
Ed Tanousd6b62632025-03-14 15:30:07 -0700520 if (section_ir == NULL) {
521 cper_print_log("RETURNING NULL2!! !!\n");
522 }
Ed Tanous73498f62025-03-05 18:03:36 -0800523 json_object_object_add(ir, "section", section_ir);
Ed Tanous8d47a372025-03-05 15:55:36 -0800524 return ir;
525}
526
Lawrence Tang617949e2022-08-08 14:21:42 +0100527//Converts a single CPER section, without a header but with a section descriptor, to JSON.
528json_object *cper_single_section_to_ir(FILE *cper_section_file)
529{
530 json_object *ir = json_object_new_object();
531
Lawrence Tang94153492022-09-05 13:07:54 +0100532 //Read the current file pointer location as base record position.
533 long base_pos = ftell(cper_section_file);
534
Lawrence Tang617949e2022-08-08 14:21:42 +0100535 //Read the section descriptor out.
536 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
537 if (fread(&section_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
538 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700539 cper_print_log(
540 "Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n");
Ed Tanous8121f7e2025-03-06 14:39:07 -0800541 json_object_put(ir);
Lawrence Tang617949e2022-08-08 14:21:42 +0100542 return NULL;
543 }
544
545 //Convert the section descriptor to IR.
546 json_object *section_descriptor_ir =
547 cper_section_descriptor_to_ir(&section_descriptor);
548 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
549
Ed Tanous73498f62025-03-05 18:03:36 -0800550 //Save our current position in the stream.
551 long position = ftell(cper_section_file);
552
553 //Read section as described by the section descriptor.
554 fseek(cper_section_file, base_pos + section_descriptor.SectionOffset,
555 SEEK_SET);
556 void *section = malloc(section_descriptor.SectionLength);
557 if (fread(section, section_descriptor.SectionLength, 1,
558 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700559 cper_print_log(
560 "Section read failed: Could not read %u bytes from global offset %d.\n",
561 section_descriptor.SectionLength,
562 section_descriptor.SectionOffset);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800563 json_object_put(ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800564 free(section);
565 return NULL;
566 }
567
568 //Seek back to our original position.
569 fseek(cper_section_file, position, SEEK_SET);
570
Lawrence Tang617949e2022-08-08 14:21:42 +0100571 //Parse the single section.
Ed Tanous73498f62025-03-05 18:03:36 -0800572 json_object *section_ir = cper_buf_section_to_ir(
573 section, section_descriptor.SectionLength, &section_descriptor);
Lawrence Tang617949e2022-08-08 14:21:42 +0100574 json_object_object_add(ir, "section", section_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800575 free(section);
Lawrence Tang617949e2022-08-08 14:21:42 +0100576 return ir;
John Chungf8fc7052024-05-03 20:05:29 +0800577}
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700578
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700579char *cperbuf_single_section_to_str_ir(const unsigned char *cper_section,
580 size_t size)
581{
Ed Tanous73498f62025-03-05 18:03:36 -0800582 json_object *jobj = cper_buf_single_section_to_ir(cper_section, size);
583 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700584
Ed Tanous73498f62025-03-05 18:03:36 -0800585 json_object_put(jobj);
586 return str;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700587}