blob: edbae56f68bb2648bf60ed82927811d0c073c679 [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);
Ed Tanous8e423942025-03-14 23:11:06 -0700119
Ed Tanousd6b62632025-03-14 15:30:07 -0700120 json_object_array_add(section_descriptors_ir,
121 section_descriptor_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800122
123 //Read the section itself.
124 json_object *section_ir = cper_buf_section_to_ir(
125 section_begin, section_descriptor->SectionLength,
126 section_descriptor);
127 json_object_array_add(sections_ir, section_ir);
128 }
129
130 //Add the header, section descriptors, and sections to a parent object.
131 json_object_object_add(parent, "sectionDescriptors",
132 section_descriptors_ir);
133 json_object_object_add(parent, "sections", sections_ir);
134
135 return parent;
136
137fail:
138 json_object_put(sections_ir);
139 json_object_put(section_descriptors_ir);
140 json_object_put(parent);
Ed Tanous50b966f2025-03-11 09:06:19 -0700141 cper_print_log("Failed to parse CPER file.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800142 return NULL;
Ed Tanous8d47a372025-03-05 15:55:36 -0800143}
144
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100145//Reads a CPER log file at the given file location, and returns an intermediate
146//JSON representation of this CPER record.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100147json_object *cper_to_ir(FILE *cper_file)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100148{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100149 //Ensure this is really a CPER log.
150 EFI_COMMON_ERROR_RECORD_HEADER header;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100151 if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1,
152 cper_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700153 cper_print_log(
154 "Invalid CPER file: Invalid length (log too short).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100155 return NULL;
156 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100157
Lawrence Tange407b4c2022-07-21 13:54:01 +0100158 //Check if the header contains the magic bytes ("CPER").
159 if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700160 cper_print_log(
161 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100162 return NULL;
163 }
Ed Tanous73498f62025-03-05 18:03:36 -0800164 fseek(cper_file, -sizeof(EFI_COMMON_ERROR_RECORD_HEADER), SEEK_CUR);
165 unsigned char *cper_buf = malloc(header.RecordLength);
Ed Tanous559780e2025-03-13 15:25:53 -0700166 int bytes_read = fread(cper_buf, 1, header.RecordLength, cper_file);
167 if (bytes_read < 0) {
168 cper_print_log("File read failed with code %u\n", bytes_read);
169 free(cper_buf);
170 return NULL;
171 }
172 if ((UINT32)bytes_read != header.RecordLength) {
173 int position = ftell(cper_file);
174 cper_print_log(
175 "File read failed file was %u bytes, expecting %u bytes from header.\n",
176 position, header.RecordLength);
Ed Tanous73498f62025-03-05 18:03:36 -0800177 free(cper_buf);
178 return NULL;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100179 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100180
Ed Tanous559780e2025-03-13 15:25:53 -0700181 json_object *ir = cper_buf_to_ir(cper_buf, bytes_read);
Ed Tanous73498f62025-03-05 18:03:36 -0800182 free(cper_buf);
183 return ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100184}
185
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700186char *cper_to_str_ir(FILE *cper_file)
187{
188 json_object *jobj = cper_to_ir(cper_file);
189 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
190
191 json_object_put(jobj);
192 return str;
193}
194
195char *cperbuf_to_str_ir(const unsigned char *cper, size_t size)
196{
197 FILE *cper_file = fmemopen((void *)cper, size, "r");
198
199 return cper_file ? cper_to_str_ir(cper_file) : NULL;
200}
201
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100202//Converts a parsed CPER record header into intermediate JSON object format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100203json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100204{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100205 json_object *header_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100206
Lawrence Tange407b4c2022-07-21 13:54:01 +0100207 //Revision/version information.
208 json_object_object_add(header_ir, "revision",
209 revision_to_ir(header->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100210
Lawrence Tange407b4c2022-07-21 13:54:01 +0100211 //Section count.
212 json_object_object_add(header_ir, "sectionCount",
213 json_object_new_int(header->SectionCount));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100214
Lawrence Tange407b4c2022-07-21 13:54:01 +0100215 //Error severity (with interpreted string version).
216 json_object *error_severity = json_object_new_object();
217 json_object_object_add(error_severity, "code",
218 json_object_new_uint64(header->ErrorSeverity));
219 json_object_object_add(error_severity, "name",
220 json_object_new_string(severity_to_string(
221 header->ErrorSeverity)));
222 json_object_object_add(header_ir, "severity", error_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100223
Lawrence Tange407b4c2022-07-21 13:54:01 +0100224 //Total length of the record (including headers) in bytes.
225 json_object_object_add(header_ir, "recordLength",
226 json_object_new_uint64(header->RecordLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100227
Lawrence Tange407b4c2022-07-21 13:54:01 +0100228 //If a timestamp exists according to validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800229 if (header->ValidationBits & 0x2) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100230 char timestamp_string[TIMESTAMP_LENGTH];
Ed Tanous596c59e2025-03-10 13:15:58 -0700231 if (timestamp_to_string(timestamp_string, TIMESTAMP_LENGTH,
Ed Tanousd6b62632025-03-14 15:30:07 -0700232 &header->TimeStamp) >= 0) {
233 json_object_object_add(
234 header_ir, "timestamp",
235 json_object_new_string(timestamp_string));
Ed Tanous596c59e2025-03-10 13:15:58 -0700236
Ed Tanousd6b62632025-03-14 15:30:07 -0700237 json_object_object_add(header_ir, "timestampIsPrecise",
238 json_object_new_boolean(
239 header->TimeStamp.Flag));
240 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100241 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100242
Lawrence Tange407b4c2022-07-21 13:54:01 +0100243 //If a platform ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800244 if (header->ValidationBits & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700245 add_guid(header_ir, "platformID", &header->PlatformID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100246 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100247
Lawrence Tange407b4c2022-07-21 13:54:01 +0100248 //If a partition ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800249 if (header->ValidationBits & 0x4) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700250 add_guid(header_ir, "partitionID", &header->PartitionID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100251 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100252
Lawrence Tange407b4c2022-07-21 13:54:01 +0100253 //Creator ID of the header.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700254 add_guid(header_ir, "creatorID", &header->CreatorID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100255 //Notification type for the header. Some defined types are available.
256 json_object *notification_type = json_object_new_object();
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700257 add_guid(notification_type, "guid", &header->NotificationType);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100258
Lawrence Tange407b4c2022-07-21 13:54:01 +0100259 //Add the human readable notification type if possible.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700260 const char *notification_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700261
262 EFI_GUID *guids[] = {
263 &gEfiEventNotificationTypeCmcGuid,
264 &gEfiEventNotificationTypeCpeGuid,
265 &gEfiEventNotificationTypeMceGuid,
266 &gEfiEventNotificationTypePcieGuid,
267 &gEfiEventNotificationTypeInitGuid,
268 &gEfiEventNotificationTypeNmiGuid,
269 &gEfiEventNotificationTypeBootGuid,
270 &gEfiEventNotificationTypeDmarGuid,
271 &gEfiEventNotificationTypeSeaGuid,
272 &gEfiEventNotificationTypeSeiGuid,
273 &gEfiEventNotificationTypePeiGuid,
274 &gEfiEventNotificationTypeCxlGuid,
275 };
276
277 const char *readable_names[] = {
278 "CMC", "CPE", "MCE", "PCIe", "INIT", "NMI",
279 "Boot", "DMAr", "SEA", "SEI", "PEI", "CXL Component"
280 };
281
282 int index = select_guid_from_list(&header->NotificationType, guids,
283 sizeof(guids) / sizeof(EFI_GUID *));
284 if (index < (int)(sizeof(readable_names) / sizeof(char *))) {
285 notification_type_readable = readable_names[index];
John Chungf8fc7052024-05-03 20:05:29 +0800286 }
Ed Tanous1a648562025-03-10 15:23:38 -0700287
Lawrence Tange407b4c2022-07-21 13:54:01 +0100288 json_object_object_add(
289 notification_type, "type",
290 json_object_new_string(notification_type_readable));
291 json_object_object_add(header_ir, "notificationType",
292 notification_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100293
Lawrence Tange407b4c2022-07-21 13:54:01 +0100294 //The record ID for this record, unique on a given system.
295 json_object_object_add(header_ir, "recordID",
296 json_object_new_uint64(header->RecordID));
297
298 //Flag for the record, and a human readable form.
299 json_object *flags = integer_to_readable_pair(
300 header->Flags,
301 sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int),
302 CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES,
303 "Unknown");
304 json_object_object_add(header_ir, "flags", flags);
305
306 //Persistence information. Outside the scope of specification, so just a uint32 here.
307 json_object_object_add(header_ir, "persistenceInfo",
308 json_object_new_uint64(header->PersistenceInfo));
309 return header_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100310}
311
312//Converts the given EFI section descriptor into JSON IR format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100313json_object *
314cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100315{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100316 json_object *section_descriptor_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100317
Lawrence Tange407b4c2022-07-21 13:54:01 +0100318 //The offset of the section from the base of the record header, length.
319 json_object_object_add(
320 section_descriptor_ir, "sectionOffset",
321 json_object_new_uint64(section_descriptor->SectionOffset));
322 json_object_object_add(
323 section_descriptor_ir, "sectionLength",
324 json_object_new_uint64(section_descriptor->SectionLength));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100325
Lawrence Tange407b4c2022-07-21 13:54:01 +0100326 //Revision.
327 json_object_object_add(section_descriptor_ir, "revision",
328 revision_to_ir(section_descriptor->Revision));
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100329
Lawrence Tange407b4c2022-07-21 13:54:01 +0100330 //Flag bits.
331 json_object *flags =
332 bitfield_to_ir(section_descriptor->SectionFlags, 8,
333 CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES);
334 json_object_object_add(section_descriptor_ir, "flags", flags);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100335
Lawrence Tange407b4c2022-07-21 13:54:01 +0100336 //Section type (GUID).
337 json_object *section_type = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100338
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700339 add_guid(section_type, "data", &section_descriptor->SectionType);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100340 //Readable section type, if possible.
Lawrence Tang580423f2022-08-24 09:37:53 +0100341 const char *section_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700342
343 CPER_SECTION_DEFINITION *section =
344 select_section_by_guid(&section_descriptor->SectionType);
345 if (section != NULL) {
346 section_type_readable = section->ReadableName;
Lawrence Tang580423f2022-08-24 09:37:53 +0100347 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100348
Lawrence Tange407b4c2022-07-21 13:54:01 +0100349 json_object_object_add(section_type, "type",
350 json_object_new_string(section_type_readable));
351 json_object_object_add(section_descriptor_ir, "sectionType",
352 section_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100353
Lawrence Tange407b4c2022-07-21 13:54:01 +0100354 //If validation bits indicate it exists, add FRU ID.
John Chungf8fc7052024-05-03 20:05:29 +0800355 if (section_descriptor->SecValidMask & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700356 add_guid(section_descriptor_ir, "fruID",
357 &section_descriptor->FruId);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100358 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100359
Lawrence Tange407b4c2022-07-21 13:54:01 +0100360 //If validation bits indicate it exists, add FRU text.
John Chungf8fc7052024-05-03 20:05:29 +0800361 if ((section_descriptor->SecValidMask & 0x2) >> 1) {
Khang D Nguyenbd1814d2025-03-31 13:07:49 +0700362 add_untrusted_string(section_descriptor_ir, "fruText",
363 section_descriptor->FruString,
364 sizeof(section_descriptor->FruString));
John Chungf8fc7052024-05-03 20:05:29 +0800365 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100366
Lawrence Tange407b4c2022-07-21 13:54:01 +0100367 //Section severity.
368 json_object *section_severity = json_object_new_object();
369 json_object_object_add(
370 section_severity, "code",
371 json_object_new_uint64(section_descriptor->Severity));
372 json_object_object_add(section_severity, "name",
373 json_object_new_string(severity_to_string(
374 section_descriptor->Severity)));
375 json_object_object_add(section_descriptor_ir, "severity",
376 section_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100377
Lawrence Tange407b4c2022-07-21 13:54:01 +0100378 return section_descriptor_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100379}
380
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800381json_object *read_section(const unsigned char *cper_section_buf, size_t size,
382 CPER_SECTION_DEFINITION *definition)
Ed Tanousd759a182025-03-07 15:51:45 -0800383{
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800384 if (definition->ToIR == NULL) {
385 return NULL;
386 }
387 json_object *section_ir = definition->ToIR(cper_section_buf, size);
Ed Tanousd6b62632025-03-14 15:30:07 -0700388 if (section_ir == NULL) {
389 return NULL;
390 }
Ed Tanousd759a182025-03-07 15:51:45 -0800391 json_object *result = json_object_new_object();
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800392 json_object_object_add(result, definition->ShortName, section_ir);
Ed Tanousd759a182025-03-07 15:51:45 -0800393 return result;
394}
395
Ed Tanous1a648562025-03-10 15:23:38 -0700396CPER_SECTION_DEFINITION *select_section_by_guid(EFI_GUID *guid)
397{
398 size_t i = 0;
399 for (; i < section_definitions_len; i++) {
400 if (guid_equal(guid, section_definitions[i].Guid)) {
401 break;
402 }
403 }
404 // It's unlikely fuzzing can reliably come up with a correct guid, given how
405 // much entropy there is. If we're in fuzzing mode, and if we haven't found
406 // a match, try to force a match so we get some coverage. Note, we still
407 // want coverage of the section failed to convert code, so treat index ==
408 // size as section failed to convert.
409#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
410 if (i == section_definitions_len) {
411 i = guid->Data1 % (section_definitions_len + 1);
412 }
413#endif
414 if (i < section_definitions_len) {
415 return &section_definitions[i];
416 }
417
418 return NULL;
419}
420
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100421//Converts the section described by a single given section descriptor.
Ed Tanous73498f62025-03-05 18:03:36 -0800422json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
423 EFI_ERROR_SECTION_DESCRIPTOR *descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100424{
Ed Tanous73498f62025-03-05 18:03:36 -0800425 if (descriptor->SectionLength > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700426 cper_print_log(
427 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100428 return NULL;
429 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100430
Lawrence Tange407b4c2022-07-21 13:54:01 +0100431 //Parse section to IR based on GUID.
432 json_object *result = NULL;
Ed Tanousb07061a2024-09-22 10:33:29 -0700433 json_object *section_ir = NULL;
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800434
Ed Tanous1a648562025-03-10 15:23:38 -0700435 CPER_SECTION_DEFINITION *section =
436 select_section_by_guid(&descriptor->SectionType);
Ed Tanousd6b62632025-03-14 15:30:07 -0700437 if (section == NULL) {
438 cper_print_log("Unknown section type guid\n");
439 } else {
Ed Tanous1a648562025-03-10 15:23:38 -0700440 result = read_section(cper_section_buf, size, section);
Ed Tanousd759a182025-03-07 15:51:45 -0800441 }
Ed Tanousb07061a2024-09-22 10:33:29 -0700442
Lawrence Tang580423f2022-08-24 09:37:53 +0100443 //Was it an unknown GUID/failed read?
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800444 if (result == NULL) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100445 //Output the data as formatted base64.
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700446 int32_t encoded_len = 0;
Ed Tanous73498f62025-03-05 18:03:36 -0800447 char *encoded = base64_encode(cper_section_buf,
448 descriptor->SectionLength,
449 &encoded_len);
Ed Tanousa7d2cdd2024-07-15 11:07:27 -0700450 if (encoded == NULL) {
Ed Tanousd6b62632025-03-14 15:30:07 -0700451 cper_print_log(
452 "Failed to allocate encode output buffer. \n");
John Chungf8fc7052024-05-03 20:05:29 +0800453 } else {
Ed Tanousb07061a2024-09-22 10:33:29 -0700454 section_ir = json_object_new_object();
455 json_object_object_add(section_ir, "data",
John Chungf8fc7052024-05-03 20:05:29 +0800456 json_object_new_string_len(
457 encoded, encoded_len));
458 free(encoded);
Ed Tanousb07061a2024-09-22 10:33:29 -0700459
460 result = json_object_new_object();
461 json_object_object_add(result, "Unknown", section_ir);
John Chungf8fc7052024-05-03 20:05:29 +0800462 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100463 }
Ed Tanousd6b62632025-03-14 15:30:07 -0700464 if (result == NULL) {
465 cper_print_log("RETURNING NULL!! !!\n");
466 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100467 return result;
Lawrence Tang617949e2022-08-08 14:21:42 +0100468}
469
Ed Tanous73498f62025-03-05 18:03:36 -0800470json_object *cper_buf_single_section_to_ir(const unsigned char *cper_buf,
471 size_t size)
Ed Tanous8d47a372025-03-05 15:55:36 -0800472{
Ed Tanous8121f7e2025-03-06 14:39:07 -0800473 const unsigned char *cper_end;
474 const unsigned char *section_begin;
475 json_object *ir;
476
477 cper_end = cper_buf + size;
Ed Tanous73498f62025-03-05 18:03:36 -0800478
479 //Read the section descriptor out.
480 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
481 if (sizeof(EFI_ERROR_SECTION_DESCRIPTOR) > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700482 cper_print_log(
483 "Size of cper buffer was too small to read section descriptor %zu\n",
484 size);
Ed Tanous8d47a372025-03-05 15:55:36 -0800485 return NULL;
486 }
Ed Tanous8121f7e2025-03-06 14:39:07 -0800487
488 ir = json_object_new_object();
Ed Tanous73498f62025-03-05 18:03:36 -0800489 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)cper_buf;
490 //Convert the section descriptor to IR.
491 json_object *section_descriptor_ir =
492 cper_section_descriptor_to_ir(section_descriptor);
493 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800494 section_begin = cper_buf + section_descriptor->SectionOffset;
Ed Tanous73498f62025-03-05 18:03:36 -0800495
Ed Tanous8121f7e2025-03-06 14:39:07 -0800496 if (section_begin + section_descriptor->SectionLength >= cper_end) {
497 json_object_put(ir);
Ed Tanous50b966f2025-03-11 09:06:19 -0700498 //cper_print_log("Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800499 return NULL;
500 }
501
502 const unsigned char *section =
503 cper_buf + section_descriptor->SectionOffset;
504
505 //Parse the single section.
506 json_object *section_ir = cper_buf_section_to_ir(
507 section, section_descriptor->SectionLength, section_descriptor);
Ed Tanousd6b62632025-03-14 15:30:07 -0700508 if (section_ir == NULL) {
509 cper_print_log("RETURNING NULL2!! !!\n");
510 }
Ed Tanous73498f62025-03-05 18:03:36 -0800511 json_object_object_add(ir, "section", section_ir);
Ed Tanous8d47a372025-03-05 15:55:36 -0800512 return ir;
513}
514
Lawrence Tang617949e2022-08-08 14:21:42 +0100515//Converts a single CPER section, without a header but with a section descriptor, to JSON.
516json_object *cper_single_section_to_ir(FILE *cper_section_file)
517{
518 json_object *ir = json_object_new_object();
519
Lawrence Tang94153492022-09-05 13:07:54 +0100520 //Read the current file pointer location as base record position.
521 long base_pos = ftell(cper_section_file);
522
Lawrence Tang617949e2022-08-08 14:21:42 +0100523 //Read the section descriptor out.
524 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
525 if (fread(&section_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
526 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700527 cper_print_log(
528 "Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n");
Ed Tanous8121f7e2025-03-06 14:39:07 -0800529 json_object_put(ir);
Lawrence Tang617949e2022-08-08 14:21:42 +0100530 return NULL;
531 }
532
533 //Convert the section descriptor to IR.
534 json_object *section_descriptor_ir =
535 cper_section_descriptor_to_ir(&section_descriptor);
536 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
537
Ed Tanous73498f62025-03-05 18:03:36 -0800538 //Save our current position in the stream.
539 long position = ftell(cper_section_file);
540
541 //Read section as described by the section descriptor.
542 fseek(cper_section_file, base_pos + section_descriptor.SectionOffset,
543 SEEK_SET);
544 void *section = malloc(section_descriptor.SectionLength);
545 if (fread(section, section_descriptor.SectionLength, 1,
546 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700547 cper_print_log(
548 "Section read failed: Could not read %u bytes from global offset %d.\n",
549 section_descriptor.SectionLength,
550 section_descriptor.SectionOffset);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800551 json_object_put(ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800552 free(section);
553 return NULL;
554 }
555
556 //Seek back to our original position.
557 fseek(cper_section_file, position, SEEK_SET);
558
Lawrence Tang617949e2022-08-08 14:21:42 +0100559 //Parse the single section.
Ed Tanous73498f62025-03-05 18:03:36 -0800560 json_object *section_ir = cper_buf_section_to_ir(
561 section, section_descriptor.SectionLength, &section_descriptor);
Lawrence Tang617949e2022-08-08 14:21:42 +0100562 json_object_object_add(ir, "section", section_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800563 free(section);
Lawrence Tang617949e2022-08-08 14:21:42 +0100564 return ir;
John Chungf8fc7052024-05-03 20:05:29 +0800565}
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700566
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700567char *cperbuf_single_section_to_str_ir(const unsigned char *cper_section,
568 size_t size)
569{
Ed Tanous73498f62025-03-05 18:03:36 -0800570 json_object *jobj = cper_buf_single_section_to_ir(cper_section, size);
571 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700572
Ed Tanous73498f62025-03-05 18:03:36 -0800573 json_object_put(jobj);
574 return str;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700575}