xref: /openbmc/libcper/tests/ir-tests.cpp (revision 50b966f7afa31fe39fda70e10eb9139cce39e025)
1 /**
2  * Defines tests for validating CPER-JSON IR output from the cper-parse library.
3  *
4  * Author: Lawrence.Tang@arm.com
5  **/
6 
7 #include <cctype>
8 #include "gtest/gtest.h"
9 #include "test-utils.hpp"
10 #include <json.h>
11 #include <charconv>
12 #include <nlohmann/json.hpp>
13 #include <filesystem>
14 #include <fstream>
15 #include <libcper/cper-parse.h>
16 #include <libcper/json-schema.h>
17 #include <libcper/generator/cper-generate.h>
18 #include <libcper/sections/cper-section.h>
19 #include <libcper/generator/sections/gen-section.h>
20 #include <format>
21 
22 namespace fs = std::filesystem;
23 
24 /*
25 * Test templates.
26 */
27 static const GEN_VALID_BITS_TEST_TYPE allValidbitsSet = ALL_VALID;
28 static const GEN_VALID_BITS_TEST_TYPE fixedValidbitsSet = SOME_VALID;
29 static const int GEN_EXAMPLES = 0;
30 
cper_create_examples(const char * section_name)31 void cper_create_examples(const char *section_name)
32 {
33 	//Generate full CPER record for the given type.
34 	fs::path file_path = LIBCPER_EXAMPLES;
35 	file_path /= section_name;
36 	fs::path cper_out = file_path.replace_extension("cperhex");
37 	fs::path json_out = file_path.replace_extension("json");
38 
39 	char *buf;
40 	size_t size;
41 	FILE *record = generate_record_memstream(&section_name, 1, &buf, &size,
42 						 0, fixedValidbitsSet);
43 
44 	// Write example CPER to disk
45 	std::ofstream outFile(cper_out, std::ios::binary);
46 	if (!outFile.is_open()) {
47 		std::cerr << "Failed to create/open CPER output file: "
48 			  << cper_out << std::endl;
49 		return;
50 	}
51 
52 	std::vector<unsigned char> file_data;
53 	fseek(record, 0, SEEK_END);
54 	size_t file_size = ftell(record);
55 	rewind(record);
56 	file_data.resize(file_size);
57 	if (fread(file_data.data(), 1, file_data.size(), record) != file_size) {
58 		std::cerr << "Failed to read CPER data from memstream."
59 			  << std::endl;
60 		FAIL();
61 		return;
62 	}
63 	for (size_t index = 0; index < file_data.size(); index++) {
64 		outFile << std::format("{:02x}", file_data[index]);
65 		if (index % 30 == 29) {
66 			outFile << "\n";
67 		}
68 	}
69 	outFile.close();
70 
71 	//Convert to IR, free resources.
72 	rewind(record);
73 	json_object *ir = cper_to_ir(record);
74 	if (ir == NULL) {
75 		std::cerr << "Empty JSON from CPER bin" << std::endl;
76 		FAIL();
77 		return;
78 	}
79 	char *str = strdup(json_object_to_json_string(ir));
80 	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
81 	if (jsonData.is_discarded()) {
82 		std::cerr << "cper_create_examples: JSON parse error:"
83 			  << std::endl;
84 	}
85 	free(str);
86 	fclose(record);
87 	free(buf);
88 
89 	//Write json output to disk
90 	std::ofstream jsonOutFile(json_out);
91 	jsonOutFile << std::setw(4) << jsonData << std::endl;
92 	jsonOutFile.close();
93 }
94 
string_to_binary(const std::string & source)95 std::vector<unsigned char> string_to_binary(const std::string &source)
96 {
97 	std::vector<unsigned char> retval;
98 	bool uppernibble = true;
99 	for (const char c : source) {
100 		unsigned char val = 0;
101 		if (c == '\n') {
102 			continue;
103 		}
104 		std::from_chars_result r = std::from_chars(&c, &c + 1, val, 16);
105 		EXPECT_TRUE(r.ec == std::error_code())
106 			<< "Invalid hex character in test file: " << c;
107 
108 		if (uppernibble) {
109 			retval.push_back(val << 4);
110 		} else {
111 			retval.back() += val;
112 		}
113 		uppernibble = !uppernibble;
114 	}
115 	return retval;
116 }
117 
118 //Tests fixed CPER sections for IR validity with an example set.
cper_example_section_ir_test(const char * section_name)119 void cper_example_section_ir_test(const char *section_name)
120 {
121 	//Open CPER record for the given type.
122 	fs::path fpath = LIBCPER_EXAMPLES;
123 	fpath /= section_name;
124 	fs::path cper = fpath.replace_extension("cperhex");
125 	fs::path json = fpath.replace_extension("json");
126 
127 	std::ifstream cper_file(cper, std::ios::binary);
128 	if (!cper_file.is_open()) {
129 		std::cerr << "Failed to open CPER file: " << cper << std::endl;
130 		FAIL() << "Failed to open CPER file";
131 		return;
132 	}
133 	std::string cper_str((std::istreambuf_iterator<char>(cper_file)),
134 			     std::istreambuf_iterator<char>());
135 
136 	std::vector<unsigned char> cper_bin = string_to_binary(cper_str);
137 	//Convert to IR, free resources.
138 	json_object *ir = cper_buf_to_ir(cper_bin.data(), cper_bin.size());
139 	if (ir == NULL) {
140 		std::cerr << "Empty JSON from CPER bin" << std::endl;
141 		FAIL();
142 		return;
143 	}
144 	const char *str = json_object_to_json_string(ir);
145 	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
146 	if (jsonData.is_discarded()) {
147 		std::cerr << "cper_example_section_ir_test: JSON parse error:"
148 			  << std::endl;
149 		FAIL() << "cper_example_section_ir_test: JSON parse error:";
150 		json_object_put(ir);
151 
152 		return;
153 	}
154 
155 	//Open json example file
156 	nlohmann::json jGolden = loadJson(json.string().c_str());
157 	if (jGolden.is_discarded()) {
158 		std::cerr << "Could not open JSON example file: " << json
159 			  << std::endl;
160 		FAIL() << "Could not open JSON example file";
161 	}
162 
163 	json_object_put(ir);
164 
165 	EXPECT_EQ(jGolden, jsonData);
166 }
167 
168 //Tests a single randomly generated CPER section of the given type to ensure CPER-JSON IR validity.
cper_log_section_ir_test(const char * section_name,int single_section,GEN_VALID_BITS_TEST_TYPE validBitsType)169 void cper_log_section_ir_test(const char *section_name, int single_section,
170 			      GEN_VALID_BITS_TEST_TYPE validBitsType)
171 {
172 	//Generate full CPER record for the given type.
173 	char *buf;
174 	size_t size;
175 	FILE *record = generate_record_memstream(&section_name, 1, &buf, &size,
176 						 single_section, validBitsType);
177 
178 	//Convert to IR, free resources.
179 	json_object *ir;
180 	if (single_section) {
181 		ir = cper_single_section_to_ir(record);
182 	} else {
183 		ir = cper_to_ir(record);
184 	}
185 
186 	char *str = strdup(json_object_to_json_string(ir));
187 	nlohmann::json jsonData = nlohmann::json::parse(str, nullptr, false);
188 	if (jsonData.is_discarded()) {
189 		std::cerr << "Could not parse json output" << std::endl;
190 	}
191 	free(str);
192 	fclose(record);
193 	free(buf);
194 
195 	//Validate against schema.
196 	std::string error_message;
197 
198 	int valid = schema_validate_from_file(LIBCPER_JSON_SPEC, jsonData,
199 					      error_message);
200 	json_object_put(ir);
201 	ASSERT_TRUE(valid)
202 		<< "IR validation test failed (single section mode = "
203 		<< single_section << ") with message: " << error_message;
204 }
205 
to_hex(char * input,size_t size)206 std::string to_hex(char *input, size_t size)
207 {
208 	std::string out;
209 	for (char c : std::span<unsigned char>((unsigned char *)input, size)) {
210 		out += std::format("{:02x}", static_cast<unsigned char>(c));
211 	}
212 	return out;
213 }
214 
215 //Checks for binary round-trip equality for a given randomly generated CPER record.
cper_log_section_binary_test(const char * section_name,int single_section,GEN_VALID_BITS_TEST_TYPE validBitsType)216 void cper_log_section_binary_test(const char *section_name, int single_section,
217 				  GEN_VALID_BITS_TEST_TYPE validBitsType)
218 {
219 	//Generate CPER record for the given type.
220 	char *buf;
221 	size_t size;
222 	FILE *record = generate_record_memstream(&section_name, 1, &buf, &size,
223 						 single_section, validBitsType);
224 	if (record == NULL) {
225 		std::cerr << "Could not generate memstream for binary test"
226 			  << std::endl;
227 		return;
228 	}
229 
230 	//Convert to IR.
231 	json_object *ir;
232 	if (single_section) {
233 		ir = cper_single_section_to_ir(record);
234 	} else {
235 		ir = cper_to_ir(record);
236 	}
237 
238 	//Now convert back to binary, and get a stream out.
239 	char *cper_buf;
240 	size_t cper_buf_size;
241 	FILE *stream = open_memstream(&cper_buf, &cper_buf_size);
242 	if (single_section) {
243 		ir_single_section_to_cper(ir, stream);
244 	} else {
245 		ir_to_cper(ir, stream);
246 	}
247 	fclose(stream);
248 
249 	std::cout << "size: " << size << ", cper_buf_size: " << cper_buf_size
250 		  << std::endl;
251 	EXPECT_EQ(to_hex(buf, size),
252 		  to_hex(cper_buf, std::min(size, cper_buf_size)))
253 		<< "Binary output was not identical to input (single section mode = "
254 		<< single_section << ").";
255 
256 	//Free everything up.
257 	fclose(record);
258 	free(buf);
259 	free(cper_buf);
260 	json_object_put(ir);
261 }
262 
263 //Tests randomly generated CPER sections for IR validity of a given type, in both single section mode and full CPER log mode.
cper_log_section_dual_ir_test(const char * section_name)264 void cper_log_section_dual_ir_test(const char *section_name)
265 {
266 	cper_log_section_ir_test(section_name, 0, allValidbitsSet);
267 	cper_log_section_ir_test(section_name, 1, allValidbitsSet);
268 	//Validate against examples
269 	cper_example_section_ir_test(section_name);
270 }
271 
272 //Tests randomly generated CPER sections for binary compatibility of a given type, in both single section mode and full CPER log mode.
cper_log_section_dual_binary_test(const char * section_name)273 void cper_log_section_dual_binary_test(const char *section_name)
274 {
275 	cper_log_section_binary_test(section_name, 0, allValidbitsSet);
276 	cper_log_section_binary_test(section_name, 1, allValidbitsSet);
277 }
278 
279 /*
280 * Non-single section assertions.
281 */
TEST(CompileTimeAssertions,TwoWayConversion)282 TEST(CompileTimeAssertions, TwoWayConversion)
283 {
284 	for (size_t i = 0; i < section_definitions_len; i++) {
285 		//If a conversion one way exists, a conversion the other way must exist.
286 		std::string err =
287 			"If a CPER conversion exists one way, there must be an equivalent method in reverse.";
288 		if (section_definitions[i].ToCPER != NULL) {
289 			ASSERT_NE(section_definitions[i].ToIR, nullptr) << err;
290 		}
291 		if (section_definitions[i].ToIR != NULL) {
292 			ASSERT_NE(section_definitions[i].ToCPER, nullptr)
293 				<< err;
294 		}
295 	}
296 }
297 
TEST(CompileTimeAssertions,ShortcodeNoSpaces)298 TEST(CompileTimeAssertions, ShortcodeNoSpaces)
299 {
300 	for (size_t i = 0; i < generator_definitions_len; i++) {
301 		for (int j = 0;
302 		     generator_definitions[i].ShortName[j + 1] != '\0'; j++) {
303 			ASSERT_FALSE(
304 				isspace(generator_definitions[i].ShortName[j]))
305 				<< "Illegal space character detected in shortcode '"
306 				<< generator_definitions[i].ShortName << "'.";
307 		}
308 	}
309 }
310 
311 /*
312 * Single section tests.
313 */
314 
315 //Generic processor tests.
TEST(GenericProcessorTests,IRValid)316 TEST(GenericProcessorTests, IRValid)
317 {
318 	cper_log_section_dual_ir_test("generic");
319 }
TEST(GenericProcessorTests,BinaryEqual)320 TEST(GenericProcessorTests, BinaryEqual)
321 {
322 	cper_log_section_dual_binary_test("generic");
323 }
324 
325 //IA32/x64 tests.
TEST(IA32x64Tests,IRValid)326 TEST(IA32x64Tests, IRValid)
327 {
328 	cper_log_section_dual_ir_test("ia32x64");
329 }
TEST(IA32x64Tests,BinaryEqual)330 TEST(IA32x64Tests, BinaryEqual)
331 {
332 	cper_log_section_dual_binary_test("ia32x64");
333 }
334 
335 // TEST(IPFTests, IRValid) {
336 //     cper_log_section_dual_ir_test("ipf");
337 // }
338 
339 //ARM tests.
TEST(ArmTests,IRValid)340 TEST(ArmTests, IRValid)
341 {
342 	cper_log_section_dual_ir_test("arm");
343 }
TEST(ArmTests,BinaryEqual)344 TEST(ArmTests, BinaryEqual)
345 {
346 	cper_log_section_dual_binary_test("arm");
347 }
348 
349 //Memory tests.
TEST(MemoryTests,IRValid)350 TEST(MemoryTests, IRValid)
351 {
352 	cper_log_section_dual_ir_test("memory");
353 }
TEST(MemoryTests,BinaryEqual)354 TEST(MemoryTests, BinaryEqual)
355 {
356 	cper_log_section_dual_binary_test("memory");
357 }
358 
359 //Memory 2 tests.
TEST(Memory2Tests,IRValid)360 TEST(Memory2Tests, IRValid)
361 {
362 	cper_log_section_dual_ir_test("memory2");
363 }
TEST(Memory2Tests,BinaryEqual)364 TEST(Memory2Tests, BinaryEqual)
365 {
366 	cper_log_section_dual_binary_test("memory2");
367 }
368 
369 //PCIe tests.
TEST(PCIeTests,IRValid)370 TEST(PCIeTests, IRValid)
371 {
372 	cper_log_section_dual_ir_test("pcie");
373 }
TEST(PCIeTests,BinaryEqual)374 TEST(PCIeTests, BinaryEqual)
375 {
376 	cper_log_section_dual_binary_test("pcie");
377 }
378 
379 //Firmware tests.
TEST(FirmwareTests,IRValid)380 TEST(FirmwareTests, IRValid)
381 {
382 	cper_log_section_dual_ir_test("firmware");
383 }
TEST(FirmwareTests,BinaryEqual)384 TEST(FirmwareTests, BinaryEqual)
385 {
386 	cper_log_section_dual_binary_test("firmware");
387 }
388 
389 //PCI Bus tests.
TEST(PCIBusTests,IRValid)390 TEST(PCIBusTests, IRValid)
391 {
392 	cper_log_section_dual_ir_test("pcibus");
393 }
TEST(PCIBusTests,BinaryEqual)394 TEST(PCIBusTests, BinaryEqual)
395 {
396 	cper_log_section_dual_binary_test("pcibus");
397 }
398 
399 //PCI Device tests.
TEST(PCIDevTests,IRValid)400 TEST(PCIDevTests, IRValid)
401 {
402 	cper_log_section_dual_ir_test("pcidev");
403 }
TEST(PCIDevTests,BinaryEqual)404 TEST(PCIDevTests, BinaryEqual)
405 {
406 	cper_log_section_dual_binary_test("pcidev");
407 }
408 
409 //Generic DMAr tests.
TEST(DMArGenericTests,IRValid)410 TEST(DMArGenericTests, IRValid)
411 {
412 	cper_log_section_dual_ir_test("dmargeneric");
413 }
TEST(DMArGenericTests,BinaryEqual)414 TEST(DMArGenericTests, BinaryEqual)
415 {
416 	cper_log_section_dual_binary_test("dmargeneric");
417 }
418 
419 //VT-d DMAr tests.
TEST(DMArVtdTests,IRValid)420 TEST(DMArVtdTests, IRValid)
421 {
422 	cper_log_section_dual_ir_test("dmarvtd");
423 }
TEST(DMArVtdTests,BinaryEqual)424 TEST(DMArVtdTests, BinaryEqual)
425 {
426 	cper_log_section_dual_binary_test("dmarvtd");
427 }
428 
429 //IOMMU DMAr tests.
TEST(DMArIOMMUTests,IRValid)430 TEST(DMArIOMMUTests, IRValid)
431 {
432 	cper_log_section_dual_ir_test("dmariommu");
433 }
TEST(DMArIOMMUTests,BinaryEqual)434 TEST(DMArIOMMUTests, BinaryEqual)
435 {
436 	cper_log_section_dual_binary_test("dmariommu");
437 }
438 
439 //CCIX PER tests.
TEST(CCIXPERTests,IRValid)440 TEST(CCIXPERTests, IRValid)
441 {
442 	cper_log_section_dual_ir_test("ccixper");
443 }
TEST(CCIXPERTests,BinaryEqual)444 TEST(CCIXPERTests, BinaryEqual)
445 {
446 	cper_log_section_dual_binary_test("ccixper");
447 }
448 
449 //CXL Protocol tests.
TEST(CXLProtocolTests,IRValid)450 TEST(CXLProtocolTests, IRValid)
451 {
452 	cper_log_section_dual_ir_test("cxlprotocol");
453 }
TEST(CXLProtocolTests,BinaryEqual)454 TEST(CXLProtocolTests, BinaryEqual)
455 {
456 	cper_log_section_dual_binary_test("cxlprotocol");
457 }
458 
459 //CXL Component tests.
TEST(CXLComponentTests,IRValid)460 TEST(CXLComponentTests, IRValid)
461 {
462 	cper_log_section_dual_ir_test("cxlcomponent-media");
463 }
TEST(CXLComponentTests,BinaryEqual)464 TEST(CXLComponentTests, BinaryEqual)
465 {
466 	cper_log_section_dual_binary_test("cxlcomponent-media");
467 }
468 
469 //NVIDIA section tests.
TEST(NVIDIASectionTests,IRValid)470 TEST(NVIDIASectionTests, IRValid)
471 {
472 	cper_log_section_dual_ir_test("nvidia");
473 }
TEST(NVIDIASectionTests,BinaryEqual)474 TEST(NVIDIASectionTests, BinaryEqual)
475 {
476 	cper_log_section_dual_binary_test("nvidia");
477 }
478 
479 //Unknown section tests.
TEST(UnknownSectionTests,IRValid)480 TEST(UnknownSectionTests, IRValid)
481 {
482 	cper_log_section_dual_ir_test("unknown");
483 }
TEST(UnknownSectionTests,BinaryEqual)484 TEST(UnknownSectionTests, BinaryEqual)
485 {
486 	cper_log_section_dual_binary_test("unknown");
487 }
488 
489 //Entrypoint for the testing program.
main()490 int main()
491 {
492 	if (GEN_EXAMPLES) {
493 		cper_create_examples("arm");
494 		cper_create_examples("ia32x64");
495 		cper_create_examples("memory");
496 		cper_create_examples("memory2");
497 		cper_create_examples("pcie");
498 		cper_create_examples("firmware");
499 		cper_create_examples("pcibus");
500 		cper_create_examples("pcidev");
501 		cper_create_examples("dmargeneric");
502 		cper_create_examples("dmarvtd");
503 		cper_create_examples("dmariommu");
504 		cper_create_examples("ccixper");
505 		cper_create_examples("cxlprotocol");
506 		cper_create_examples("cxlcomponent-media");
507 		cper_create_examples("nvidia");
508 		cper_create_examples("unknown");
509 	}
510 	testing::InitGoogleTest();
511 	return RUN_ALL_TESTS();
512 }
513