blob: af7d42406579e5e4f9ebbc982e73f352861903c4 [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(
Ed Tanous63d3d642025-04-11 12:24:33 -070041 "Invalid CPER file: Invalid header (not enough bytes).\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(
Ed Tanous63d3d642025-04-11 12:24:33 -070051 "Invalid CPER file: Invalid header (incorrect signature). %x\n",
52 header->SignatureStart);
Ed Tanous73498f62025-03-05 18:03:36 -080053 goto fail;
54 }
55 if (header->SectionCount == 0) {
Ed Tanous50b966f2025-03-11 09:06:19 -070056 cper_print_log(
57 "Invalid CPER file: Invalid section count (0).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080058 goto fail;
59 }
60 if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
Ed Tanous50b966f2025-03-11 09:06:19 -070061 cper_print_log(
62 "Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080063 goto fail;
64 }
65
66 //Create the header JSON object from the read bytes.
67 parent = json_object_new_object();
68 header_ir = cper_header_to_ir(header);
69
70 json_object_object_add(parent, "header", header_ir);
71
72 //Read the appropriate number of section descriptors & sections, and convert them into IR format.
73 section_descriptors_ir = json_object_new_array();
74 sections_ir = json_object_new_array();
75 for (int i = 0; i < header->SectionCount; i++) {
76 //Create the section descriptor.
77 if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
Ed Tanous50b966f2025-03-11 09:06:19 -070078 cper_print_log(
79 "Invalid number of section headers: Header states %d sections, could not read section %d.\n",
80 header->SectionCount, i + 1);
Ed Tanous73498f62025-03-05 18:03:36 -080081 goto fail;
82 }
83
84 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
85 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)(pos);
86 pos += sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
87 remaining -= sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
88
89 if (section_descriptor->SectionOffset > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -070090 cper_print_log(
91 "Invalid section descriptor: Section offset > size.\n");
Ed Tanous73498f62025-03-05 18:03:36 -080092 goto fail;
93 }
94
95 if (section_descriptor->SectionLength <= 0) {
Ed Tanous50b966f2025-03-11 09:06:19 -070096 cper_print_log(
97 "Invalid section descriptor: Section length <= 0.\n");
Ed Tanous73498f62025-03-05 18:03:36 -080098 goto fail;
99 }
100
101 if (section_descriptor->SectionOffset >
102 UINT_MAX - section_descriptor->SectionLength) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700103 cper_print_log(
104 "Invalid section descriptor: Section offset + length would overflow.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800105 goto fail;
106 }
107
108 if (section_descriptor->SectionOffset +
109 section_descriptor->SectionLength >
110 size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700111 cper_print_log(
112 "Invalid section descriptor: Section offset + length > size.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800113 goto fail;
114 }
115
116 const unsigned char *section_begin =
117 cper_buf + section_descriptor->SectionOffset;
Ed Tanousd6b62632025-03-14 15:30:07 -0700118 json_object *section_descriptor_ir =
119 cper_section_descriptor_to_ir(section_descriptor);
Ed Tanous8e423942025-03-14 23:11:06 -0700120
Ed Tanousd6b62632025-03-14 15:30:07 -0700121 json_object_array_add(section_descriptors_ir,
122 section_descriptor_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800123
124 //Read the section itself.
125 json_object *section_ir = cper_buf_section_to_ir(
126 section_begin, section_descriptor->SectionLength,
127 section_descriptor);
128 json_object_array_add(sections_ir, section_ir);
129 }
130
131 //Add the header, section descriptors, and sections to a parent object.
132 json_object_object_add(parent, "sectionDescriptors",
133 section_descriptors_ir);
134 json_object_object_add(parent, "sections", sections_ir);
135
136 return parent;
137
138fail:
139 json_object_put(sections_ir);
140 json_object_put(section_descriptors_ir);
141 json_object_put(parent);
Ed Tanous50b966f2025-03-11 09:06:19 -0700142 cper_print_log("Failed to parse CPER file.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800143 return NULL;
Ed Tanous8d47a372025-03-05 15:55:36 -0800144}
145
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100146//Reads a CPER log file at the given file location, and returns an intermediate
147//JSON representation of this CPER record.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100148json_object *cper_to_ir(FILE *cper_file)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100149{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100150 //Ensure this is really a CPER log.
151 EFI_COMMON_ERROR_RECORD_HEADER header;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100152 if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1,
153 cper_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700154 cper_print_log(
155 "Invalid CPER file: Invalid length (log too short).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100156 return NULL;
157 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100158
Lawrence Tange407b4c2022-07-21 13:54:01 +0100159 //Check if the header contains the magic bytes ("CPER").
160 if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700161 cper_print_log(
162 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100163 return NULL;
164 }
Ed Tanous73498f62025-03-05 18:03:36 -0800165 fseek(cper_file, -sizeof(EFI_COMMON_ERROR_RECORD_HEADER), SEEK_CUR);
166 unsigned char *cper_buf = malloc(header.RecordLength);
Ed Tanous559780e2025-03-13 15:25:53 -0700167 int bytes_read = fread(cper_buf, 1, header.RecordLength, cper_file);
168 if (bytes_read < 0) {
169 cper_print_log("File read failed with code %u\n", bytes_read);
170 free(cper_buf);
171 return NULL;
172 }
173 if ((UINT32)bytes_read != header.RecordLength) {
174 int position = ftell(cper_file);
175 cper_print_log(
176 "File read failed file was %u bytes, expecting %u bytes from header.\n",
177 position, header.RecordLength);
Ed Tanous73498f62025-03-05 18:03:36 -0800178 free(cper_buf);
179 return NULL;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100180 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100181
Ed Tanous559780e2025-03-13 15:25:53 -0700182 json_object *ir = cper_buf_to_ir(cper_buf, bytes_read);
Ed Tanous73498f62025-03-05 18:03:36 -0800183 free(cper_buf);
184 return ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100185}
186
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700187char *cper_to_str_ir(FILE *cper_file)
188{
189 json_object *jobj = cper_to_ir(cper_file);
190 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
191
192 json_object_put(jobj);
193 return str;
194}
195
196char *cperbuf_to_str_ir(const unsigned char *cper, size_t size)
197{
198 FILE *cper_file = fmemopen((void *)cper, size, "r");
199
200 return cper_file ? cper_to_str_ir(cper_file) : NULL;
201}
202
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100203//Converts a parsed CPER record header into intermediate JSON object format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100204json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100205{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100206 json_object *header_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100207
Lawrence Tange407b4c2022-07-21 13:54:01 +0100208 //Revision/version information.
209 json_object_object_add(header_ir, "revision",
210 revision_to_ir(header->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100211
Lawrence Tange407b4c2022-07-21 13:54:01 +0100212 //Section count.
213 json_object_object_add(header_ir, "sectionCount",
214 json_object_new_int(header->SectionCount));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100215
Lawrence Tange407b4c2022-07-21 13:54:01 +0100216 //Error severity (with interpreted string version).
217 json_object *error_severity = json_object_new_object();
218 json_object_object_add(error_severity, "code",
219 json_object_new_uint64(header->ErrorSeverity));
220 json_object_object_add(error_severity, "name",
221 json_object_new_string(severity_to_string(
222 header->ErrorSeverity)));
223 json_object_object_add(header_ir, "severity", error_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100224
Lawrence Tange407b4c2022-07-21 13:54:01 +0100225 //Total length of the record (including headers) in bytes.
226 json_object_object_add(header_ir, "recordLength",
227 json_object_new_uint64(header->RecordLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100228
Lawrence Tange407b4c2022-07-21 13:54:01 +0100229 //If a timestamp exists according to validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800230 if (header->ValidationBits & 0x2) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100231 char timestamp_string[TIMESTAMP_LENGTH];
Ed Tanous596c59e2025-03-10 13:15:58 -0700232 if (timestamp_to_string(timestamp_string, TIMESTAMP_LENGTH,
Ed Tanousd6b62632025-03-14 15:30:07 -0700233 &header->TimeStamp) >= 0) {
234 json_object_object_add(
235 header_ir, "timestamp",
236 json_object_new_string(timestamp_string));
Ed Tanous596c59e2025-03-10 13:15:58 -0700237
Ed Tanousd6b62632025-03-14 15:30:07 -0700238 json_object_object_add(header_ir, "timestampIsPrecise",
239 json_object_new_boolean(
240 header->TimeStamp.Flag));
241 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100242 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100243
Lawrence Tange407b4c2022-07-21 13:54:01 +0100244 //If a platform ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800245 if (header->ValidationBits & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700246 add_guid(header_ir, "platformID", &header->PlatformID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100247 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100248
Lawrence Tange407b4c2022-07-21 13:54:01 +0100249 //If a partition ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800250 if (header->ValidationBits & 0x4) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700251 add_guid(header_ir, "partitionID", &header->PartitionID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100252 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100253
Lawrence Tange407b4c2022-07-21 13:54:01 +0100254 //Creator ID of the header.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700255 add_guid(header_ir, "creatorID", &header->CreatorID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100256 //Notification type for the header. Some defined types are available.
257 json_object *notification_type = json_object_new_object();
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700258 add_guid(notification_type, "guid", &header->NotificationType);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100259
Lawrence Tange407b4c2022-07-21 13:54:01 +0100260 //Add the human readable notification type if possible.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700261 const char *notification_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700262
263 EFI_GUID *guids[] = {
264 &gEfiEventNotificationTypeCmcGuid,
265 &gEfiEventNotificationTypeCpeGuid,
266 &gEfiEventNotificationTypeMceGuid,
267 &gEfiEventNotificationTypePcieGuid,
268 &gEfiEventNotificationTypeInitGuid,
269 &gEfiEventNotificationTypeNmiGuid,
270 &gEfiEventNotificationTypeBootGuid,
271 &gEfiEventNotificationTypeDmarGuid,
272 &gEfiEventNotificationTypeSeaGuid,
273 &gEfiEventNotificationTypeSeiGuid,
274 &gEfiEventNotificationTypePeiGuid,
275 &gEfiEventNotificationTypeCxlGuid,
276 };
277
278 const char *readable_names[] = {
279 "CMC", "CPE", "MCE", "PCIe", "INIT", "NMI",
280 "Boot", "DMAr", "SEA", "SEI", "PEI", "CXL Component"
281 };
282
283 int index = select_guid_from_list(&header->NotificationType, guids,
284 sizeof(guids) / sizeof(EFI_GUID *));
285 if (index < (int)(sizeof(readable_names) / sizeof(char *))) {
286 notification_type_readable = readable_names[index];
John Chungf8fc7052024-05-03 20:05:29 +0800287 }
Ed Tanous1a648562025-03-10 15:23:38 -0700288
Lawrence Tange407b4c2022-07-21 13:54:01 +0100289 json_object_object_add(
290 notification_type, "type",
291 json_object_new_string(notification_type_readable));
292 json_object_object_add(header_ir, "notificationType",
293 notification_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100294
Lawrence Tange407b4c2022-07-21 13:54:01 +0100295 //The record ID for this record, unique on a given system.
296 json_object_object_add(header_ir, "recordID",
297 json_object_new_uint64(header->RecordID));
298
299 //Flag for the record, and a human readable form.
300 json_object *flags = integer_to_readable_pair(
301 header->Flags,
302 sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int),
303 CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES,
304 "Unknown");
305 json_object_object_add(header_ir, "flags", flags);
306
307 //Persistence information. Outside the scope of specification, so just a uint32 here.
308 json_object_object_add(header_ir, "persistenceInfo",
309 json_object_new_uint64(header->PersistenceInfo));
310 return header_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100311}
312
313//Converts the given EFI section descriptor into JSON IR format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100314json_object *
315cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100316{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100317 json_object *section_descriptor_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100318
Lawrence Tange407b4c2022-07-21 13:54:01 +0100319 //The offset of the section from the base of the record header, length.
320 json_object_object_add(
321 section_descriptor_ir, "sectionOffset",
322 json_object_new_uint64(section_descriptor->SectionOffset));
323 json_object_object_add(
324 section_descriptor_ir, "sectionLength",
325 json_object_new_uint64(section_descriptor->SectionLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100326
Lawrence Tange407b4c2022-07-21 13:54:01 +0100327 //Revision.
328 json_object_object_add(section_descriptor_ir, "revision",
329 revision_to_ir(section_descriptor->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100330
Lawrence Tange407b4c2022-07-21 13:54:01 +0100331 //Flag bits.
332 json_object *flags =
333 bitfield_to_ir(section_descriptor->SectionFlags, 8,
334 CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES);
335 json_object_object_add(section_descriptor_ir, "flags", flags);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100336
Lawrence Tange407b4c2022-07-21 13:54:01 +0100337 //Section type (GUID).
338 json_object *section_type = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100339
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700340 add_guid(section_type, "data", &section_descriptor->SectionType);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100341 //Readable section type, if possible.
Lawrence Tang580423f2022-08-24 09:37:53 +0100342 const char *section_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700343
344 CPER_SECTION_DEFINITION *section =
345 select_section_by_guid(&section_descriptor->SectionType);
346 if (section != NULL) {
347 section_type_readable = section->ReadableName;
Lawrence Tang580423f2022-08-24 09:37:53 +0100348 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100349
Lawrence Tange407b4c2022-07-21 13:54:01 +0100350 json_object_object_add(section_type, "type",
351 json_object_new_string(section_type_readable));
352 json_object_object_add(section_descriptor_ir, "sectionType",
353 section_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100354
Lawrence Tange407b4c2022-07-21 13:54:01 +0100355 //If validation bits indicate it exists, add FRU ID.
John Chungf8fc7052024-05-03 20:05:29 +0800356 if (section_descriptor->SecValidMask & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700357 add_guid(section_descriptor_ir, "fruID",
358 &section_descriptor->FruId);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100359 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100360
Lawrence Tange407b4c2022-07-21 13:54:01 +0100361 //If validation bits indicate it exists, add FRU text.
John Chungf8fc7052024-05-03 20:05:29 +0800362 if ((section_descriptor->SecValidMask & 0x2) >> 1) {
Khang D Nguyenbd1814d2025-03-31 13:07:49 +0700363 add_untrusted_string(section_descriptor_ir, "fruText",
364 section_descriptor->FruString,
365 sizeof(section_descriptor->FruString));
John Chungf8fc7052024-05-03 20:05:29 +0800366 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100367
Lawrence Tange407b4c2022-07-21 13:54:01 +0100368 //Section severity.
369 json_object *section_severity = json_object_new_object();
370 json_object_object_add(
371 section_severity, "code",
372 json_object_new_uint64(section_descriptor->Severity));
373 json_object_object_add(section_severity, "name",
374 json_object_new_string(severity_to_string(
375 section_descriptor->Severity)));
376 json_object_object_add(section_descriptor_ir, "severity",
377 section_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100378
Lawrence Tange407b4c2022-07-21 13:54:01 +0100379 return section_descriptor_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100380}
381
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800382json_object *read_section(const unsigned char *cper_section_buf, size_t size,
383 CPER_SECTION_DEFINITION *definition)
Ed Tanousd759a182025-03-07 15:51:45 -0800384{
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800385 if (definition->ToIR == NULL) {
386 return NULL;
387 }
388 json_object *section_ir = definition->ToIR(cper_section_buf, size);
Ed Tanousd6b62632025-03-14 15:30:07 -0700389 if (section_ir == NULL) {
390 return NULL;
391 }
Ed Tanousd759a182025-03-07 15:51:45 -0800392 json_object *result = json_object_new_object();
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800393 json_object_object_add(result, definition->ShortName, section_ir);
Ed Tanousd759a182025-03-07 15:51:45 -0800394 return result;
395}
396
Ed Tanous1a648562025-03-10 15:23:38 -0700397CPER_SECTION_DEFINITION *select_section_by_guid(EFI_GUID *guid)
398{
399 size_t i = 0;
400 for (; i < section_definitions_len; i++) {
401 if (guid_equal(guid, section_definitions[i].Guid)) {
402 break;
403 }
404 }
405 // It's unlikely fuzzing can reliably come up with a correct guid, given how
406 // much entropy there is. If we're in fuzzing mode, and if we haven't found
407 // a match, try to force a match so we get some coverage. Note, we still
408 // want coverage of the section failed to convert code, so treat index ==
409 // size as section failed to convert.
410#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
411 if (i == section_definitions_len) {
412 i = guid->Data1 % (section_definitions_len + 1);
413 }
414#endif
415 if (i < section_definitions_len) {
416 return &section_definitions[i];
417 }
418
419 return NULL;
420}
421
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100422//Converts the section described by a single given section descriptor.
Ed Tanous73498f62025-03-05 18:03:36 -0800423json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
424 EFI_ERROR_SECTION_DESCRIPTOR *descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100425{
Ed Tanous73498f62025-03-05 18:03:36 -0800426 if (descriptor->SectionLength > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700427 cper_print_log(
428 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100429 return NULL;
430 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100431
Lawrence Tange407b4c2022-07-21 13:54:01 +0100432 //Parse section to IR based on GUID.
433 json_object *result = NULL;
Ed Tanousb07061a2024-09-22 10:33:29 -0700434 json_object *section_ir = NULL;
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800435
Ed Tanous1a648562025-03-10 15:23:38 -0700436 CPER_SECTION_DEFINITION *section =
437 select_section_by_guid(&descriptor->SectionType);
Ed Tanousd6b62632025-03-14 15:30:07 -0700438 if (section == NULL) {
439 cper_print_log("Unknown section type guid\n");
440 } else {
Ed Tanous1a648562025-03-10 15:23:38 -0700441 result = read_section(cper_section_buf, size, section);
Ed Tanousd759a182025-03-07 15:51:45 -0800442 }
Ed Tanousb07061a2024-09-22 10:33:29 -0700443
Lawrence Tang580423f2022-08-24 09:37:53 +0100444 //Was it an unknown GUID/failed read?
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800445 if (result == NULL) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100446 //Output the data as formatted base64.
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700447 int32_t encoded_len = 0;
Ed Tanous73498f62025-03-05 18:03:36 -0800448 char *encoded = base64_encode(cper_section_buf,
449 descriptor->SectionLength,
450 &encoded_len);
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700451 if (encoded == NULL) {
Ed Tanousd6b62632025-03-14 15:30:07 -0700452 cper_print_log(
453 "Failed to allocate encode output buffer. \n");
John Chungf8fc7052024-05-03 20:05:29 +0800454 } else {
Ed Tanousb07061a2024-09-22 10:33:29 -0700455 section_ir = json_object_new_object();
456 json_object_object_add(section_ir, "data",
John Chungf8fc7052024-05-03 20:05:29 +0800457 json_object_new_string_len(
458 encoded, encoded_len));
459 free(encoded);
Ed Tanousb07061a2024-09-22 10:33:29 -0700460
461 result = json_object_new_object();
462 json_object_object_add(result, "Unknown", section_ir);
John Chungf8fc7052024-05-03 20:05:29 +0800463 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100464 }
Ed Tanousd6b62632025-03-14 15:30:07 -0700465 if (result == NULL) {
466 cper_print_log("RETURNING NULL!! !!\n");
467 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100468 return result;
Lawrence Tang617949e2022-08-08 14:21:42 +0100469}
470
Ed Tanous73498f62025-03-05 18:03:36 -0800471json_object *cper_buf_single_section_to_ir(const unsigned char *cper_buf,
472 size_t size)
Ed Tanous8d47a372025-03-05 15:55:36 -0800473{
Ed Tanous8121f7e2025-03-06 14:39:07 -0800474 const unsigned char *cper_end;
475 const unsigned char *section_begin;
476 json_object *ir;
477
478 cper_end = cper_buf + size;
Ed Tanous73498f62025-03-05 18:03:36 -0800479
480 //Read the section descriptor out.
481 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
482 if (sizeof(EFI_ERROR_SECTION_DESCRIPTOR) > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700483 cper_print_log(
484 "Size of cper buffer was too small to read section descriptor %zu\n",
485 size);
Ed Tanous8d47a372025-03-05 15:55:36 -0800486 return NULL;
487 }
Ed Tanous8121f7e2025-03-06 14:39:07 -0800488
489 ir = json_object_new_object();
Ed Tanous73498f62025-03-05 18:03:36 -0800490 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)cper_buf;
491 //Convert the section descriptor to IR.
492 json_object *section_descriptor_ir =
493 cper_section_descriptor_to_ir(section_descriptor);
494 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800495 section_begin = cper_buf + section_descriptor->SectionOffset;
Ed Tanous73498f62025-03-05 18:03:36 -0800496
Ed Tanous8121f7e2025-03-06 14:39:07 -0800497 if (section_begin + section_descriptor->SectionLength >= cper_end) {
498 json_object_put(ir);
Ed Tanous50b966f2025-03-11 09:06:19 -0700499 //cper_print_log("Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800500 return NULL;
501 }
502
503 const unsigned char *section =
504 cper_buf + section_descriptor->SectionOffset;
505
506 //Parse the single section.
507 json_object *section_ir = cper_buf_section_to_ir(
508 section, section_descriptor->SectionLength, section_descriptor);
Ed Tanousd6b62632025-03-14 15:30:07 -0700509 if (section_ir == NULL) {
510 cper_print_log("RETURNING NULL2!! !!\n");
511 }
Ed Tanous73498f62025-03-05 18:03:36 -0800512 json_object_object_add(ir, "section", section_ir);
Ed Tanous8d47a372025-03-05 15:55:36 -0800513 return ir;
514}
515
Lawrence Tang617949e2022-08-08 14:21:42 +0100516//Converts a single CPER section, without a header but with a section descriptor, to JSON.
517json_object *cper_single_section_to_ir(FILE *cper_section_file)
518{
519 json_object *ir = json_object_new_object();
520
Lawrence Tang94153492022-09-05 13:07:54 +0100521 //Read the current file pointer location as base record position.
522 long base_pos = ftell(cper_section_file);
523
Lawrence Tang617949e2022-08-08 14:21:42 +0100524 //Read the section descriptor out.
525 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
526 if (fread(&section_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
527 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700528 cper_print_log(
529 "Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n");
Ed Tanous8121f7e2025-03-06 14:39:07 -0800530 json_object_put(ir);
Lawrence Tang617949e2022-08-08 14:21:42 +0100531 return NULL;
532 }
533
534 //Convert the section descriptor to IR.
535 json_object *section_descriptor_ir =
536 cper_section_descriptor_to_ir(&section_descriptor);
537 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
538
Ed Tanous73498f62025-03-05 18:03:36 -0800539 //Save our current position in the stream.
540 long position = ftell(cper_section_file);
541
542 //Read section as described by the section descriptor.
543 fseek(cper_section_file, base_pos + section_descriptor.SectionOffset,
544 SEEK_SET);
545 void *section = malloc(section_descriptor.SectionLength);
546 if (fread(section, section_descriptor.SectionLength, 1,
547 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700548 cper_print_log(
549 "Section read failed: Could not read %u bytes from global offset %d.\n",
550 section_descriptor.SectionLength,
551 section_descriptor.SectionOffset);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800552 json_object_put(ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800553 free(section);
554 return NULL;
555 }
556
557 //Seek back to our original position.
558 fseek(cper_section_file, position, SEEK_SET);
559
Lawrence Tang617949e2022-08-08 14:21:42 +0100560 //Parse the single section.
Ed Tanous73498f62025-03-05 18:03:36 -0800561 json_object *section_ir = cper_buf_section_to_ir(
562 section, section_descriptor.SectionLength, &section_descriptor);
Lawrence Tang617949e2022-08-08 14:21:42 +0100563 json_object_object_add(ir, "section", section_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800564 free(section);
Lawrence Tang617949e2022-08-08 14:21:42 +0100565 return ir;
John Chungf8fc7052024-05-03 20:05:29 +0800566}
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700567
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700568char *cperbuf_single_section_to_str_ir(const unsigned char *cper_section,
569 size_t size)
570{
Ed Tanous73498f62025-03-05 18:03:36 -0800571 json_object *jobj = cper_buf_single_section_to_ir(cper_section, size);
572 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700573
Ed Tanous73498f62025-03-05 18:03:36 -0800574 json_object_put(jobj);
575 return str;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700576}