blob: 7b6353aaec8c96344b95e8ef0713fe0772cbd90c [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
Ed Tanous3e728062025-03-17 20:55:20 -070029static int header_signature_valid(EFI_COMMON_ERROR_RECORD_HEADER *header)
30{
31 if (header->SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
32 cper_print_log(
33 "Invalid CPER file: Invalid header (incorrect signature). %x\n",
34 header->SignatureStart);
35 return 0;
36 }
37 if (header->SignatureEnd != EFI_ERROR_RECORD_SIGNATURE_END) {
38 cper_print_log(
39 "Invalid CPER file: Invalid header (incorrect signature end). %x\n",
40 header->SignatureEnd);
41 return 0;
42 }
43 if (header->SectionCount == 0) {
44 cper_print_log(
45 "Invalid CPER file: Invalid section count (0).\n");
46 return 0;
47 }
48 return 1;
49}
50
51int header_valid(const char *cper_buf, size_t size)
52{
53 if (size < sizeof(EFI_COMMON_ERROR_RECORD_HEADER)) {
54 return 0;
55 }
56 EFI_COMMON_ERROR_RECORD_HEADER *header =
57 (EFI_COMMON_ERROR_RECORD_HEADER *)cper_buf;
58 if (!header_signature_valid(header)) {
59 return 0;
60 }
61 return header_signature_valid(header);
62}
63
Ed Tanous73498f62025-03-05 18:03:36 -080064json_object *cper_buf_to_ir(const unsigned char *cper_buf, size_t size)
Ed Tanous8d47a372025-03-05 15:55:36 -080065{
Ed Tanous73498f62025-03-05 18:03:36 -080066 json_object *parent = NULL;
67 json_object *header_ir = NULL;
68 json_object *section_descriptors_ir = NULL;
69 json_object *sections_ir = NULL;
70
71 const unsigned char *pos = cper_buf;
72 unsigned int remaining = size;
Ed Tanous3e728062025-03-17 20:55:20 -070073 if (size < sizeof(EFI_COMMON_ERROR_RECORD_HEADER)) {
Ed Tanous73498f62025-03-05 18:03:36 -080074 goto fail;
Ed Tanous8d47a372025-03-05 15:55:36 -080075 }
Ed Tanous3e728062025-03-17 20:55:20 -070076 EFI_COMMON_ERROR_RECORD_HEADER *header =
77 (EFI_COMMON_ERROR_RECORD_HEADER *)cper_buf;
78 if (!header_signature_valid(header)) {
79 goto fail;
80 }
Ed Tanous73498f62025-03-05 18:03:36 -080081 pos += sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
82 remaining -= sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
Ed Tanous3e728062025-03-17 20:55:20 -070083
Ed Tanous73498f62025-03-05 18:03:36 -080084 if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
Ed Tanous50b966f2025-03-11 09:06:19 -070085 cper_print_log(
86 "Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -080087 goto fail;
88 }
89
90 //Create the header JSON object from the read bytes.
91 parent = json_object_new_object();
92 header_ir = cper_header_to_ir(header);
93
94 json_object_object_add(parent, "header", header_ir);
95
96 //Read the appropriate number of section descriptors & sections, and convert them into IR format.
97 section_descriptors_ir = json_object_new_array();
98 sections_ir = json_object_new_array();
99 for (int i = 0; i < header->SectionCount; i++) {
100 //Create the section descriptor.
101 if (remaining < sizeof(EFI_ERROR_SECTION_DESCRIPTOR)) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700102 cper_print_log(
103 "Invalid number of section headers: Header states %d sections, could not read section %d.\n",
104 header->SectionCount, i + 1);
Ed Tanous73498f62025-03-05 18:03:36 -0800105 goto fail;
106 }
107
108 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
109 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)(pos);
110 pos += sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
111 remaining -= sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
112
113 if (section_descriptor->SectionOffset > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700114 cper_print_log(
115 "Invalid section descriptor: Section offset > size.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800116 goto fail;
117 }
118
119 if (section_descriptor->SectionLength <= 0) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700120 cper_print_log(
121 "Invalid section descriptor: Section length <= 0.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800122 goto fail;
123 }
124
125 if (section_descriptor->SectionOffset >
126 UINT_MAX - section_descriptor->SectionLength) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700127 cper_print_log(
128 "Invalid section descriptor: Section offset + length would overflow.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800129 goto fail;
130 }
131
132 if (section_descriptor->SectionOffset +
133 section_descriptor->SectionLength >
134 size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700135 cper_print_log(
136 "Invalid section descriptor: Section offset + length > size.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800137 goto fail;
138 }
139
140 const unsigned char *section_begin =
141 cper_buf + section_descriptor->SectionOffset;
Ed Tanousd6b62632025-03-14 15:30:07 -0700142 json_object *section_descriptor_ir =
143 cper_section_descriptor_to_ir(section_descriptor);
Ed Tanous8e423942025-03-14 23:11:06 -0700144
Ed Tanousd6b62632025-03-14 15:30:07 -0700145 json_object_array_add(section_descriptors_ir,
146 section_descriptor_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800147
148 //Read the section itself.
149 json_object *section_ir = cper_buf_section_to_ir(
150 section_begin, section_descriptor->SectionLength,
151 section_descriptor);
152 json_object_array_add(sections_ir, section_ir);
153 }
154
155 //Add the header, section descriptors, and sections to a parent object.
156 json_object_object_add(parent, "sectionDescriptors",
157 section_descriptors_ir);
158 json_object_object_add(parent, "sections", sections_ir);
159
160 return parent;
161
162fail:
163 json_object_put(sections_ir);
164 json_object_put(section_descriptors_ir);
165 json_object_put(parent);
Ed Tanous50b966f2025-03-11 09:06:19 -0700166 cper_print_log("Failed to parse CPER file.\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800167 return NULL;
Ed Tanous8d47a372025-03-05 15:55:36 -0800168}
169
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100170//Reads a CPER log file at the given file location, and returns an intermediate
171//JSON representation of this CPER record.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100172json_object *cper_to_ir(FILE *cper_file)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100173{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100174 //Ensure this is really a CPER log.
175 EFI_COMMON_ERROR_RECORD_HEADER header;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100176 if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1,
177 cper_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700178 cper_print_log(
179 "Invalid CPER file: Invalid length (log too short).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100180 return NULL;
181 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100182
Lawrence Tange407b4c2022-07-21 13:54:01 +0100183 //Check if the header contains the magic bytes ("CPER").
184 if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700185 cper_print_log(
186 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100187 return NULL;
188 }
Ed Tanous73498f62025-03-05 18:03:36 -0800189 fseek(cper_file, -sizeof(EFI_COMMON_ERROR_RECORD_HEADER), SEEK_CUR);
190 unsigned char *cper_buf = malloc(header.RecordLength);
Ed Tanous559780e2025-03-13 15:25:53 -0700191 int bytes_read = fread(cper_buf, 1, header.RecordLength, cper_file);
192 if (bytes_read < 0) {
193 cper_print_log("File read failed with code %u\n", bytes_read);
194 free(cper_buf);
195 return NULL;
196 }
197 if ((UINT32)bytes_read != header.RecordLength) {
198 int position = ftell(cper_file);
199 cper_print_log(
200 "File read failed file was %u bytes, expecting %u bytes from header.\n",
201 position, header.RecordLength);
Ed Tanous73498f62025-03-05 18:03:36 -0800202 free(cper_buf);
203 return NULL;
Lawrence Tange407b4c2022-07-21 13:54:01 +0100204 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100205
Ed Tanous559780e2025-03-13 15:25:53 -0700206 json_object *ir = cper_buf_to_ir(cper_buf, bytes_read);
Ed Tanous73498f62025-03-05 18:03:36 -0800207 free(cper_buf);
208 return ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100209}
210
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700211char *cper_to_str_ir(FILE *cper_file)
212{
213 json_object *jobj = cper_to_ir(cper_file);
214 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
215
216 json_object_put(jobj);
217 return str;
218}
219
220char *cperbuf_to_str_ir(const unsigned char *cper, size_t size)
221{
222 FILE *cper_file = fmemopen((void *)cper, size, "r");
223
224 return cper_file ? cper_to_str_ir(cper_file) : NULL;
225}
226
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100227//Converts a parsed CPER record header into intermediate JSON object format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100228json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100229{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100230 json_object *header_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100231
Lawrence Tange407b4c2022-07-21 13:54:01 +0100232 //Revision/version information.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800233 json_object *revision = revision_to_ir(header->Revision);
234 json_object_object_add(header_ir, "revision", revision);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100235
Lawrence Tange407b4c2022-07-21 13:54:01 +0100236 //Section count.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800237 add_int(header_ir, "sectionCount", header->SectionCount);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100238
Lawrence Tange407b4c2022-07-21 13:54:01 +0100239 //Error severity (with interpreted string version).
240 json_object *error_severity = json_object_new_object();
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800241 add_uint(error_severity, "code", header->ErrorSeverity);
242 const char *severity_name = severity_to_string(header->ErrorSeverity);
243 add_string(error_severity, "name", severity_name);
244
Lawrence Tange407b4c2022-07-21 13:54:01 +0100245 json_object_object_add(header_ir, "severity", error_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100246
Lawrence Tange407b4c2022-07-21 13:54:01 +0100247 //Total length of the record (including headers) in bytes.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800248 add_uint(header_ir, "recordLength", header->RecordLength);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100249
Lawrence Tange407b4c2022-07-21 13:54:01 +0100250 //If a timestamp exists according to validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800251 if (header->ValidationBits & 0x2) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100252 char timestamp_string[TIMESTAMP_LENGTH];
Ed Tanous596c59e2025-03-10 13:15:58 -0700253 if (timestamp_to_string(timestamp_string, TIMESTAMP_LENGTH,
Ed Tanousd6b62632025-03-14 15:30:07 -0700254 &header->TimeStamp) >= 0) {
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800255 add_string(header_ir, "timestamp", timestamp_string);
Ed Tanous596c59e2025-03-10 13:15:58 -0700256
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800257 add_bool(header_ir, "timestampIsPrecise",
258 header->TimeStamp.Flag);
Ed Tanousd6b62632025-03-14 15:30:07 -0700259 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100260 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100261
Lawrence Tange407b4c2022-07-21 13:54:01 +0100262 //If a platform ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800263 if (header->ValidationBits & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700264 add_guid(header_ir, "platformID", &header->PlatformID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100265 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100266
Lawrence Tange407b4c2022-07-21 13:54:01 +0100267 //If a partition ID exists according to the validation bits, then add it.
John Chungf8fc7052024-05-03 20:05:29 +0800268 if (header->ValidationBits & 0x4) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700269 add_guid(header_ir, "partitionID", &header->PartitionID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100270 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100271
Lawrence Tange407b4c2022-07-21 13:54:01 +0100272 //Creator ID of the header.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700273 add_guid(header_ir, "creatorID", &header->CreatorID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100274 //Notification type for the header. Some defined types are available.
275 json_object *notification_type = json_object_new_object();
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700276 add_guid(notification_type, "guid", &header->NotificationType);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100277
Lawrence Tange407b4c2022-07-21 13:54:01 +0100278 //Add the human readable notification type if possible.
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700279 const char *notification_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700280
281 EFI_GUID *guids[] = {
282 &gEfiEventNotificationTypeCmcGuid,
283 &gEfiEventNotificationTypeCpeGuid,
284 &gEfiEventNotificationTypeMceGuid,
285 &gEfiEventNotificationTypePcieGuid,
286 &gEfiEventNotificationTypeInitGuid,
287 &gEfiEventNotificationTypeNmiGuid,
288 &gEfiEventNotificationTypeBootGuid,
289 &gEfiEventNotificationTypeDmarGuid,
290 &gEfiEventNotificationTypeSeaGuid,
291 &gEfiEventNotificationTypeSeiGuid,
292 &gEfiEventNotificationTypePeiGuid,
293 &gEfiEventNotificationTypeCxlGuid,
294 };
295
296 const char *readable_names[] = {
297 "CMC", "CPE", "MCE", "PCIe", "INIT", "NMI",
298 "Boot", "DMAr", "SEA", "SEI", "PEI", "CXL Component"
299 };
300
301 int index = select_guid_from_list(&header->NotificationType, guids,
302 sizeof(guids) / sizeof(EFI_GUID *));
303 if (index < (int)(sizeof(readable_names) / sizeof(char *))) {
304 notification_type_readable = readable_names[index];
John Chungf8fc7052024-05-03 20:05:29 +0800305 }
Ed Tanous1a648562025-03-10 15:23:38 -0700306
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800307 add_string(notification_type, "type", notification_type_readable);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100308 json_object_object_add(header_ir, "notificationType",
309 notification_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100310
Lawrence Tange407b4c2022-07-21 13:54:01 +0100311 //The record ID for this record, unique on a given system.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800312 add_uint(header_ir, "recordID", header->RecordID);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100313
314 //Flag for the record, and a human readable form.
315 json_object *flags = integer_to_readable_pair(
316 header->Flags,
317 sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int),
318 CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES,
319 "Unknown");
320 json_object_object_add(header_ir, "flags", flags);
321
322 //Persistence information. Outside the scope of specification, so just a uint32 here.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800323 add_uint(header_ir, "persistenceInfo", header->PersistenceInfo);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100324 return header_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100325}
326
327//Converts the given EFI section descriptor into JSON IR format.
Lawrence Tange407b4c2022-07-21 13:54:01 +0100328json_object *
329cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100330{
Lawrence Tange407b4c2022-07-21 13:54:01 +0100331 json_object *section_descriptor_ir = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100332
Lawrence Tange407b4c2022-07-21 13:54:01 +0100333 //The offset of the section from the base of the record header, length.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800334 add_uint(section_descriptor_ir, "sectionOffset",
335 section_descriptor->SectionOffset);
336 add_uint(section_descriptor_ir, "sectionLength",
337 section_descriptor->SectionLength);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100338
Lawrence Tange407b4c2022-07-21 13:54:01 +0100339 //Revision.
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800340 json_object *revision = revision_to_ir(section_descriptor->Revision);
341 json_object_object_add(section_descriptor_ir, "revision", revision);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100342
Lawrence Tange407b4c2022-07-21 13:54:01 +0100343 //Flag bits.
344 json_object *flags =
345 bitfield_to_ir(section_descriptor->SectionFlags, 8,
346 CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES);
347 json_object_object_add(section_descriptor_ir, "flags", flags);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100348
Lawrence Tange407b4c2022-07-21 13:54:01 +0100349 //Section type (GUID).
350 json_object *section_type = json_object_new_object();
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100351
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700352 add_guid(section_type, "data", &section_descriptor->SectionType);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100353 //Readable section type, if possible.
Lawrence Tang580423f2022-08-24 09:37:53 +0100354 const char *section_type_readable = "Unknown";
Ed Tanous1a648562025-03-10 15:23:38 -0700355
356 CPER_SECTION_DEFINITION *section =
357 select_section_by_guid(&section_descriptor->SectionType);
358 if (section != NULL) {
359 section_type_readable = section->ReadableName;
Lawrence Tang580423f2022-08-24 09:37:53 +0100360 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100361
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800362 add_string(section_type, "type", section_type_readable);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100363 json_object_object_add(section_descriptor_ir, "sectionType",
364 section_type);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100365
Lawrence Tange407b4c2022-07-21 13:54:01 +0100366 //If validation bits indicate it exists, add FRU ID.
John Chungf8fc7052024-05-03 20:05:29 +0800367 if (section_descriptor->SecValidMask & 0x1) {
Ed Tanousc2ebddd2025-03-09 10:07:01 -0700368 add_guid(section_descriptor_ir, "fruID",
369 &section_descriptor->FruId);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100370 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100371
Lawrence Tange407b4c2022-07-21 13:54:01 +0100372 //If validation bits indicate it exists, add FRU text.
John Chungf8fc7052024-05-03 20:05:29 +0800373 if ((section_descriptor->SecValidMask & 0x2) >> 1) {
Khang D Nguyenbd1814d2025-03-31 13:07:49 +0700374 add_untrusted_string(section_descriptor_ir, "fruText",
375 section_descriptor->FruString,
376 sizeof(section_descriptor->FruString));
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();
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800381 add_uint(section_severity, "code", section_descriptor->Severity);
382 add_string(section_severity, "name",
383 severity_to_string(section_descriptor->Severity));
Lawrence Tange407b4c2022-07-21 13:54:01 +0100384 json_object_object_add(section_descriptor_ir, "severity",
385 section_severity);
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100386
Lawrence Tange407b4c2022-07-21 13:54:01 +0100387 return section_descriptor_ir;
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100388}
389
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800390json_object *read_section(const unsigned char *cper_section_buf, size_t size,
391 CPER_SECTION_DEFINITION *definition)
Ed Tanousd759a182025-03-07 15:51:45 -0800392{
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800393 if (definition->ToIR == NULL) {
394 return NULL;
395 }
Aushim Nagarkattiad6c8802025-06-18 16:45:28 -0700396 char *cper_description = NULL;
397
398 json_object *section_ir =
399 definition->ToIR(cper_section_buf, size, &cper_description);
Ed Tanousd6b62632025-03-14 15:30:07 -0700400 if (section_ir == NULL) {
John Chungfa6c53a2025-07-28 16:45:23 -0500401 free(cper_description);
Ed Tanousd6b62632025-03-14 15:30:07 -0700402 return NULL;
403 }
Ed Tanousd759a182025-03-07 15:51:45 -0800404 json_object *result = json_object_new_object();
Aushim Nagarkattiad6c8802025-06-18 16:45:28 -0700405 if (cper_description != NULL) {
Ed Tanous6c5d2f32026-02-02 15:18:15 -0800406 add_string(result, "message", cper_description);
Aushim Nagarkattiad6c8802025-06-18 16:45:28 -0700407 free(cper_description);
408 }
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800409 json_object_object_add(result, definition->ShortName, section_ir);
Ed Tanousd759a182025-03-07 15:51:45 -0800410 return result;
411}
412
Ed Tanous1a648562025-03-10 15:23:38 -0700413CPER_SECTION_DEFINITION *select_section_by_guid(EFI_GUID *guid)
414{
415 size_t i = 0;
416 for (; i < section_definitions_len; i++) {
417 if (guid_equal(guid, section_definitions[i].Guid)) {
418 break;
419 }
420 }
421 // It's unlikely fuzzing can reliably come up with a correct guid, given how
422 // much entropy there is. If we're in fuzzing mode, and if we haven't found
423 // a match, try to force a match so we get some coverage. Note, we still
424 // want coverage of the section failed to convert code, so treat index ==
425 // size as section failed to convert.
426#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
427 if (i == section_definitions_len) {
428 i = guid->Data1 % (section_definitions_len + 1);
429 }
430#endif
431 if (i < section_definitions_len) {
432 return &section_definitions[i];
433 }
434
435 return NULL;
436}
437
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100438//Converts the section described by a single given section descriptor.
Ed Tanous73498f62025-03-05 18:03:36 -0800439json_object *cper_buf_section_to_ir(const void *cper_section_buf, size_t size,
440 EFI_ERROR_SECTION_DESCRIPTOR *descriptor)
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100441{
Ed Tanous73498f62025-03-05 18:03:36 -0800442 if (descriptor->SectionLength > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700443 cper_print_log(
444 "Invalid CPER file: Invalid header (incorrect signature).\n");
Lawrence Tange407b4c2022-07-21 13:54:01 +0100445 return NULL;
446 }
Lawrence Tang1b0b00e2022-07-05 10:33:10 +0100447
Lawrence Tange407b4c2022-07-21 13:54:01 +0100448 //Parse section to IR based on GUID.
449 json_object *result = NULL;
Ed Tanousb07061a2024-09-22 10:33:29 -0700450 json_object *section_ir = NULL;
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800451
Ed Tanous1a648562025-03-10 15:23:38 -0700452 CPER_SECTION_DEFINITION *section =
453 select_section_by_guid(&descriptor->SectionType);
Ed Tanousd6b62632025-03-14 15:30:07 -0700454 if (section == NULL) {
455 cper_print_log("Unknown section type guid\n");
456 } else {
Ed Tanous1a648562025-03-10 15:23:38 -0700457 result = read_section(cper_section_buf, size, section);
Ed Tanousd759a182025-03-07 15:51:45 -0800458 }
Ed Tanousb07061a2024-09-22 10:33:29 -0700459
Lawrence Tang580423f2022-08-24 09:37:53 +0100460 //Was it an unknown GUID/failed read?
Ed Tanous12dbd4f2025-03-08 19:05:01 -0800461 if (result == NULL) {
Lawrence Tange407b4c2022-07-21 13:54:01 +0100462 //Output the data as formatted base64.
Ed Tanous3f810e52026-02-04 12:04:10 -0800463 section_ir = json_object_new_object();
464 add_binary_base64(section_ir, "data", cper_section_buf,
465 descriptor->SectionLength);
Ed Tanousb07061a2024-09-22 10:33:29 -0700466
Ed Tanous3f810e52026-02-04 12:04:10 -0800467 result = json_object_new_object();
468 json_object_object_add(result, "Unknown", section_ir);
Lawrence Tange407b4c2022-07-21 13:54:01 +0100469 }
Ed Tanousd6b62632025-03-14 15:30:07 -0700470 if (result == NULL) {
471 cper_print_log("RETURNING NULL!! !!\n");
472 }
Lawrence Tange407b4c2022-07-21 13:54:01 +0100473 return result;
Lawrence Tang617949e2022-08-08 14:21:42 +0100474}
475
Ed Tanous73498f62025-03-05 18:03:36 -0800476json_object *cper_buf_single_section_to_ir(const unsigned char *cper_buf,
477 size_t size)
Ed Tanous8d47a372025-03-05 15:55:36 -0800478{
Ed Tanous8121f7e2025-03-06 14:39:07 -0800479 const unsigned char *cper_end;
480 const unsigned char *section_begin;
481 json_object *ir;
482
483 cper_end = cper_buf + size;
Ed Tanous73498f62025-03-05 18:03:36 -0800484
485 //Read the section descriptor out.
486 EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor;
487 if (sizeof(EFI_ERROR_SECTION_DESCRIPTOR) > size) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700488 cper_print_log(
489 "Size of cper buffer was too small to read section descriptor %zu\n",
490 size);
Ed Tanous8d47a372025-03-05 15:55:36 -0800491 return NULL;
492 }
Ed Tanous8121f7e2025-03-06 14:39:07 -0800493
494 ir = json_object_new_object();
Ed Tanous73498f62025-03-05 18:03:36 -0800495 section_descriptor = (EFI_ERROR_SECTION_DESCRIPTOR *)cper_buf;
496 //Convert the section descriptor to IR.
497 json_object *section_descriptor_ir =
498 cper_section_descriptor_to_ir(section_descriptor);
499 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800500 section_begin = cper_buf + section_descriptor->SectionOffset;
Ed Tanous73498f62025-03-05 18:03:36 -0800501
John Chungfa6c53a2025-07-28 16:45:23 -0500502 if (section_begin + section_descriptor->SectionLength > cper_end) {
Ed Tanous8121f7e2025-03-06 14:39:07 -0800503 json_object_put(ir);
Ed Tanous50b966f2025-03-11 09:06:19 -0700504 //cper_print_log("Invalid CPER file: Invalid section descriptor (section offset + length > size).\n");
Ed Tanous73498f62025-03-05 18:03:36 -0800505 return NULL;
506 }
507
508 const unsigned char *section =
509 cper_buf + section_descriptor->SectionOffset;
510
511 //Parse the single section.
512 json_object *section_ir = cper_buf_section_to_ir(
513 section, section_descriptor->SectionLength, section_descriptor);
Ed Tanousd6b62632025-03-14 15:30:07 -0700514 if (section_ir == NULL) {
515 cper_print_log("RETURNING NULL2!! !!\n");
516 }
Ed Tanous73498f62025-03-05 18:03:36 -0800517 json_object_object_add(ir, "section", section_ir);
Ed Tanous8d47a372025-03-05 15:55:36 -0800518 return ir;
519}
520
Lawrence Tang617949e2022-08-08 14:21:42 +0100521//Converts a single CPER section, without a header but with a section descriptor, to JSON.
522json_object *cper_single_section_to_ir(FILE *cper_section_file)
523{
524 json_object *ir = json_object_new_object();
525
Lawrence Tang94153492022-09-05 13:07:54 +0100526 //Read the current file pointer location as base record position.
527 long base_pos = ftell(cper_section_file);
528
Lawrence Tang617949e2022-08-08 14:21:42 +0100529 //Read the section descriptor out.
530 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor;
531 if (fread(&section_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1,
532 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700533 cper_print_log(
534 "Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n");
Ed Tanous8121f7e2025-03-06 14:39:07 -0800535 json_object_put(ir);
Lawrence Tang617949e2022-08-08 14:21:42 +0100536 return NULL;
537 }
538
539 //Convert the section descriptor to IR.
540 json_object *section_descriptor_ir =
541 cper_section_descriptor_to_ir(&section_descriptor);
542 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir);
543
Ed Tanous73498f62025-03-05 18:03:36 -0800544 //Save our current position in the stream.
545 long position = ftell(cper_section_file);
546
547 //Read section as described by the section descriptor.
548 fseek(cper_section_file, base_pos + section_descriptor.SectionOffset,
549 SEEK_SET);
550 void *section = malloc(section_descriptor.SectionLength);
551 if (fread(section, section_descriptor.SectionLength, 1,
552 cper_section_file) != 1) {
Ed Tanous50b966f2025-03-11 09:06:19 -0700553 cper_print_log(
554 "Section read failed: Could not read %u bytes from global offset %d.\n",
555 section_descriptor.SectionLength,
556 section_descriptor.SectionOffset);
Ed Tanous8121f7e2025-03-06 14:39:07 -0800557 json_object_put(ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800558 free(section);
559 return NULL;
560 }
561
562 //Seek back to our original position.
563 fseek(cper_section_file, position, SEEK_SET);
564
Lawrence Tang617949e2022-08-08 14:21:42 +0100565 //Parse the single section.
Ed Tanous73498f62025-03-05 18:03:36 -0800566 json_object *section_ir = cper_buf_section_to_ir(
567 section, section_descriptor.SectionLength, &section_descriptor);
Lawrence Tang617949e2022-08-08 14:21:42 +0100568 json_object_object_add(ir, "section", section_ir);
Ed Tanous73498f62025-03-05 18:03:36 -0800569 free(section);
Lawrence Tang617949e2022-08-08 14:21:42 +0100570 return ir;
John Chungf8fc7052024-05-03 20:05:29 +0800571}
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700572
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700573char *cperbuf_single_section_to_str_ir(const unsigned char *cper_section,
574 size_t size)
575{
Ed Tanous73498f62025-03-05 18:03:36 -0800576 json_object *jobj = cper_buf_single_section_to_ir(cper_section, size);
577 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700578
Ed Tanous73498f62025-03-05 18:03:36 -0800579 json_object_put(jobj);
580 return str;
Karthik Rajagopalan5220c9b2024-08-08 00:24:44 -0700581}