From 11585071a7a90a436927c85e5dc4aedfa897f8ca Mon Sep 17 00:00:00 2001 From: VEP333 Date: Tue, 8 Sep 2026 05:40:18 +0000 Subject: [PATCH] Add JSON and HTML reporting for Devicetree memory-map tests Enhance the Devicetree standalone log parser flow to generate JSON and HTML reports for reserved-memory UEFI memory type validation, DTB 8-byte alignment validation, and Memory Reservation Block validation against EfiReservedMemoryType. The generated reports include individual test results, PASS/FAIL status, failure details, and overall compliance in the consolidated output. Signed-off-by: VEP333 Change-Id: I524e8bbac373bbd70889bf105efad84d2d15ea65 --- common/log_parser/acs-results-schema.json | 8 + common/log_parser/enrich_suite_json.py | 2 + common/log_parser/main_log_parser.sh | 29 ++ common/log_parser/merge_jsons.py | 13 +- .../standalone_tests/logs_to_json.py | 284 ++++++++++++++++++ common/log_parser/suite_registry.json | 44 ++- common/log_parser/test_categoryDT.json | 40 ++- common/tools/test_categoryDT-schema.json | 10 +- 8 files changed, 424 insertions(+), 6 deletions(-) diff --git a/common/log_parser/acs-results-schema.json b/common/log_parser/acs-results-schema.json index 411afa48..5f99b359 100644 --- a/common/log_parser/acs-results-schema.json +++ b/common/log_parser/acs-results-schema.json @@ -1639,6 +1639,9 @@ "Suite_Name: Mandatory : DT_VALIDATE_compliance": { "type": "string" }, + "Suite_Name: Mandatory : DTB_ALIGNMENT_compliance": { + "type": "string" + }, "Suite_Name: Mandatory : ETHTOOL_TEST_compliance": { "type": "string" }, @@ -1666,6 +1669,9 @@ "Suite_Name: Mandatory : READ_WRITE_CHECK_BLK_DEVICES_compliance": { "type": "string" }, + "Suite_Name: Mandatory : RESERVED_MEMORY_MAP_compliance": { + "type": "string" + }, "Suite_Name: Recommended : BSA_compliance": { "type": "string" }, @@ -1728,9 +1734,11 @@ "required": [ "Suite_Name: Mandatory : Capsule Update_compliance", "Suite_Name: Mandatory : DT_VALIDATE_compliance", + "Suite_Name: Mandatory : DTB_ALIGNMENT_compliance", "Suite_Name: Mandatory : ETHTOOL_TEST_compliance", "Suite_Name: Mandatory : EBBR-FWTS_compliance", "Suite_Name: Mandatory : READ_WRITE_CHECK_BLK_DEVICES_compliance", + "Suite_Name: Mandatory : RESERVED_MEMORY_MAP_compliance", "Suite_Name: Mandatory : EBBR-SCT_compliance", "Suite_Name: Mandatory : OS_ethtool_test_linux-Fedora-40_compliance", "Suite_Name: Mandatory : OS_ethtool_test_linux-Tumbleweed_compliance", diff --git a/common/log_parser/enrich_suite_json.py b/common/log_parser/enrich_suite_json.py index 7cf6bfeb..8d488307 100644 --- a/common/log_parser/enrich_suite_json.py +++ b/common/log_parser/enrich_suite_json.py @@ -68,6 +68,8 @@ "PSCI", "READ-WRITE-CHECK-BLK-DEVICES", "RUNTIME-DEV-MAP", + "RESERVED-MEMORY-MAP", + "DTB-ALIGNMENT", "SMBIOS", } diff --git a/common/log_parser/main_log_parser.sh b/common/log_parser/main_log_parser.sh index 0c1e4a03..a8f5e974 100755 --- a/common/log_parser/main_log_parser.sh +++ b/common/log_parser/main_log_parser.sh @@ -634,6 +634,35 @@ if [ $YOCTO_FLAG_PRESENT -eq 1 ]; then echo -e "${RED}ERROR: Runtime device mapping log parsing to json failed.${NC}" fi fi + # 10) RESERVED MEMORY MAP + RESERVED_MEM_MAP_LOG="$LINUX_TOOLS_LOGS_PATH/reserved_memory_map_test.log" + RESERVED_MEM_MAP_JSON="$JSONS_DIR/reserved_memory_map.json" + if check_file "$RESERVED_MEM_MAP_LOG" "M"; then + python3 "$SCRIPTS_PATH/standalone_tests/logs_to_json.py" \ + "$RESERVED_MEM_MAP_LOG" \ + "$RESERVED_MEM_MAP_JSON" + if [ $? -eq 0 ]; then + Standalone_JSONS+=("$RESERVED_MEM_MAP_JSON") + apply_waivers "Standalone" "$RESERVED_MEM_MAP_JSON" + else + echo -e "${RED}ERROR: Reserved memory map log parsing to json failed.${NC}" + fi + fi + + # 11) DTB ALIGNMENT + DTB_ALIGNMENT_LOG="$LINUX_TOOLS_LOGS_PATH/dtb_alignment_test.log" + DTB_ALIGNMENT_JSON="$JSONS_DIR/dtb_alignment.json" + if check_file "$DTB_ALIGNMENT_LOG" "M"; then + python3 "$SCRIPTS_PATH/standalone_tests/logs_to_json.py" \ + "$DTB_ALIGNMENT_LOG" \ + "$DTB_ALIGNMENT_JSON" + if [ $? -eq 0 ]; then + Standalone_JSONS+=("$DTB_ALIGNMENT_JSON") + apply_waivers "Standalone" "$DTB_ALIGNMENT_JSON" + else + echo -e "${RED}ERROR: DTB alignment log parsing to json failed.${NC}" + fi + fi # Now generate a single STANDALONE HTML if [ ${#Standalone_JSONS[@]} -gt 0 ]; then diff --git a/common/log_parser/merge_jsons.py b/common/log_parser/merge_jsons.py index 9bdff197..5f837c67 100755 --- a/common/log_parser/merge_jsons.py +++ b/common/log_parser/merge_jsons.py @@ -430,6 +430,12 @@ def merge_json_files(json_files, output_file): elif "NETWORK_BOOT" in fn or "network_boot" in fn.lower(): section_name = "Suite_Name: Network boot" suite_key = "NETWORK_BOOT" + elif "reserved_memory_map" in fn.lower(): + section_name = "Suite_Name: Reserved Memory Map" + suite_key = "RESERVED_MEMORY_MAP" + elif "dtb_alignment" in fn.lower(): + section_name = "Suite_Name: DTB Alignment" + suite_key = "DTB_ALIGNMENT" elif "SMBIOS" in fn: section_name = "Suite_Name: SMBIOS" suite_key = "SMBIOS" @@ -472,7 +478,8 @@ def merge_json_files(json_files, output_file): lookup_suite_key = suite_key.lower() standalone_aliases = { "dt_kselftest", "dt_validate", "ethtool_test", - "read_write_check_blk_devices", "psci", "capsule update", "network_boot", "smbios", "runtime_dev_map" + "read_write_check_blk_devices", "psci", "capsule update", "network_boot", "smbios", "runtime_dev_map" , + "reserved_memory_map", "dtb_alignment" } if lookup_suite_key in standalone_aliases or lookup_suite_key.startswith("os_"): lookup_suite_key = "standalone" @@ -813,7 +820,9 @@ def _is_missing(val: str) -> bool: "Suite_Name: PSCI": "Suite_Name: Standalone", "Suite_Name: SMBIOS": "Suite_Name: Standalone", "Suite_Name: Network boot": "Suite_Name: Standalone", - "Suite_Name: Runtime device mapping": "Suite_Name: Standalone" + "Suite_Name: Runtime device mapping": "Suite_Name: Standalone", + "Suite_Name: Reserved Memory Map": "Suite_Name: Standalone", + "Suite_Name: DTB Alignment": "Suite_Name: Standalone", } def _entry_to_list(entry): diff --git a/common/log_parser/standalone_tests/logs_to_json.py b/common/log_parser/standalone_tests/logs_to_json.py index e31a8d08..b1d4dd90 100755 --- a/common/log_parser/standalone_tests/logs_to_json.py +++ b/common/log_parser/standalone_tests/logs_to_json.py @@ -66,6 +66,17 @@ "Test_suite_description": "Coverage of EBBR requirements", "Test_case_description": "Checks runtime device mapping conflict" }, + "reserved_memory_map": { + "Test_suite": "Devicetree Reserved Memory Validation", + "Test_suite_description": "Validates reserved-memory regions in the Devicetree against expected UEFI memory type and reservation requirements.", + "Test_case_description": "Checks static reserved-memory nodes and memory reservation block entries for correct mapping and reservation behavior." + }, + + "dtb_alignment": { + "Test_suite": "Devicetree Blob Alignment Validation", + "Test_suite_description": "Validates that the Devicetree Blob address satisfies the required alignment constraints.", + "Test_case_description": "Checks that the DTB table address is non-zero and aligned to an 8-byte boundary." + }, } def create_subtest(subtest_number, description, status, reason=""): @@ -936,7 +947,276 @@ def add_subtest(desc, status, reason=""): "test_results": [current_test], "suite_summary": suite_summary } +def parse_resv_mem_map(log_data): + test_suite_key = "reserved_memory_map" + mapping = test_suite_mapping[test_suite_key] + + suite_summary = { + "total_passed": 0, + "total_failed": 0, + "total_skipped": 0, + "total_aborted": 0, + "total_warnings": 0, + "total_failed_with_waiver": 0 + } + + current_test = { + "Test_suite": mapping["Test_suite"], + "Test_suite_description": mapping["Test_suite_description"], + "Test_case": test_suite_key, + "Test_case_description": mapping["Test_case_description"], + "subtests": [], + "test_suite_summary": suite_summary.copy() + } + + subtest_number = 1 + + reason_lines_resv = [] + reason_lines_mrb = [] + overall_result = None + overall_errors = [] + warning_lines = [] + + for line in log_data: + line = line.strip() + + if line.startswith("ERROR: Reserved-memory"): + reason_lines_resv.append(line) + + if line.startswith("ERROR: Memory Reservation Block"): + reason_lines_mrb.append(line) + + if line.startswith("ERROR:"): + overall_errors.append(line) + + if line.startswith("DEBUG:"): + warning_lines.append(line) + + if line.startswith("RESULTS:"): + overall_result = line.split(":", 1)[1].strip() + + if ( + "Reserved Memory Map Compliance Test:" in line + or "Memory Reservation Block Compliance Test:" in line + ): + match = re.match( + r"^INFO:\s+(.+?):\s+(PASSED|FAILED|SKIPPED)", + line, + ) + + if not match: + continue + + test_desc = match.group(1) + result = match.group(2) + + if test_desc == "Reserved Memory Map Compliance Test": + reason = ",".join(reason_lines_resv) + else: + reason = ",".join(reason_lines_mrb) + + sub = create_subtest( + subtest_number, + test_desc, + result, + reason=reason, + ) + + current_test["subtests"].append(sub) + + update_suite_summary( + current_test["test_suite_summary"], + result, + ) + + update_suite_summary( + suite_summary, + result, + ) + + subtest_number += 1 + + # Preserve the final RESULTS status. + # + # A component may already have produced a subtest, for example: + # Reserved Memory Map Compliance Test: PASSED + # + # The MRB check can then terminate with an ERROR followed by: + # RESULTS: FAILED + # + # Do not discard that terminal failure merely because another + # component subtest already exists. + if overall_result: + existing_failed = ( + current_test["test_suite_summary"]["total_failed"] > 0 + ) + + if overall_result == "FAILED" and not existing_failed: + existing_descs = { + sub["sub_Test_Description"] + for sub in current_test["subtests"] + } + + # If the reserved-memory result exists but the MRB result + # was never emitted, attribute the terminal failure to MRB. + if ( + "Reserved Memory Map Compliance Test" in existing_descs + and "Memory Reservation Block Compliance Test" + not in existing_descs + ): + test_desc = "Memory Reservation Block Compliance Test" + reason = ",".join(reason_lines_mrb or overall_errors) + else: + test_desc = "Reserved Memory Map Compliance Test" + reason = ",".join(overall_errors) + + sub = create_subtest( + subtest_number, + test_desc, + "FAILED", + reason=reason, + ) + + current_test["subtests"].append(sub) + + update_suite_summary( + current_test["test_suite_summary"], + "FAILED", + ) + + update_suite_summary( + suite_summary, + "FAILED", + ) + + subtest_number += 1 + + # Original early-exit fallback: no component result at all. + elif not current_test["subtests"]: + if overall_result == "WARNINGS": + reason = ",".join(warning_lines) + else: + reason = ",".join(overall_errors) + + sub = create_subtest( + subtest_number, + "Reserved Memory Map Compliance Test", + overall_result, + reason=reason, + ) + + current_test["subtests"].append(sub) + + update_suite_summary( + current_test["test_suite_summary"], + overall_result, + ) + + update_suite_summary( + suite_summary, + overall_result, + ) + else: + reason = "; ".join( + overall_errors + ["Incomplete log: final RESULTS line is missing"] + ) + current_test["subtests"].append(create_subtest( + subtest_number, + "Reserved-memory log completion", + "FAILED", + reason=reason, + )) + update_suite_summary(current_test["test_suite_summary"], "FAILED") + update_suite_summary(suite_summary, "FAILED") + + # Remove empty reason arrays + for subtest in current_test["subtests"]: + subres = subtest["sub_test_result"] + + for key in [ + "pass_reasons", + "fail_reasons", + "abort_reasons", + "skip_reasons", + "warning_reasons" + ]: + if not subres.get(key): + subres.pop(key, None) + + return { + "test_results": [current_test], + "suite_summary": suite_summary + } +def parse_dtb_alignment(log_data): + test_suite_key = "dtb_alignment" + mapping = test_suite_mapping[test_suite_key] + test_desc="Devicetree Blob Alignment Validation" # the address needs to be aligned to 8 bytes, so the test is a single subtest + + suite_summary = { + "total_passed": 0, + "total_failed": 0, + "total_skipped": 0, + "total_aborted": 0, + "total_warnings": 0, + "total_failed_with_waiver": 0 + } + + current_test = { + "Test_suite": mapping["Test_suite"], + "Test_suite_description": mapping["Test_suite_description"], + "Test_case": test_suite_key, + "Test_case_description": mapping["Test_case_description"], + "subtests": [], + "test_suite_summary": suite_summary.copy() + } + + subtest_number = 1 + fail_reasons = "" + result = "FAILED" + for line in log_data: + line = line.strip() + if line.startswith("ERROR:"): + fail_reasons += line + "\n" + if line.startswith("RESULT:"): + result = line.split(":", 1)[1].strip() + "ED" + sub = create_subtest( + subtest_number, + test_desc, + result, + reason=fail_reasons.strip() if fail_reasons else None + ) + + current_test["subtests"].append(sub) + + update_suite_summary( + current_test["test_suite_summary"], + result + ) + + update_suite_summary( + suite_summary, + result + ) + + for subtest in current_test["subtests"]: + subres = subtest["sub_test_result"] + + for key in [ + "pass_reasons", + "fail_reasons", + "abort_reasons", + "skip_reasons", + "warning_reasons" + ]: + if not subres.get(key): + subres.pop(key, None) + + return { + "test_results": [current_test], + "suite_summary": suite_summary + } + ############################################################################### # PSCI Checker Parse ############################################################################### @@ -1370,6 +1650,10 @@ def parse_single_log(log_file_path): return parse_network_boot_log(log_data) elif re.search(r'Testing Runtime Device Mapping Conflict Test', log_content): return parse_runtime_dev_map_conflict(log_data) + elif re.search(r'Testing Devicetree Reserved Memory Map Compliance Test', log_content): + return parse_resv_mem_map(log_data) + elif re.search(r'Starting DTB alignment test', log_content): + return parse_dtb_alignment(log_data) else: raise ValueError("Unknown or unsupported standalone log format.") diff --git a/common/log_parser/suite_registry.json b/common/log_parser/suite_registry.json index e38d2bc6..a3c9ccd0 100644 --- a/common/log_parser/suite_registry.json +++ b/common/log_parser/suite_registry.json @@ -198,7 +198,9 @@ "PSCI", "SMBIOS", "NETWORK-BOOT", - "RUNTIME-DEV-MAP" + "RUNTIME-DEV-MAP", + "RESERVED-MEMORY-MAP", + "DTB-ALIGNMENT" ] }, { @@ -229,6 +231,34 @@ "detailed_html": "standalone_tests_detailed.html", "summary_html": "standalone_tests_summary.html" }, + { + "canonical": "RESERVED-MEMORY-MAP", + "aliases": ["RESERVED-MEMORY-MAP", "RESERVEDMEMORYMAP", "RESERVED_MEMORY_MAP"], + "modes": ["DT"], + "requirement_key": "RESERVED_MEMORY_MAP", + "requirements": {"DT": "M"}, + "waiver_suite": "Standalone", + "logs_to_json": "standalone_tests/logs_to_json.py", + "json_to_html": "standalone_tests/json_to_html.py", + "json_output": "reserved_memory_map.json", + "schema": "acs-results-schema.json#/definitions/raw_standalone_suite", + "detailed_html": "standalone_tests_detailed.html", + "summary_html": "standalone_tests_summary.html" + }, + { + "canonical": "DTB-ALIGNMENT", + "aliases": ["DTB-ALIGNMENT", "DTBALIGNMENT", "DTB_ALIGNMENT"], + "modes": ["DT"], + "requirement_key": "DTB_ALIGNMENT", + "requirements": {"DT": "M"}, + "waiver_suite": "Standalone", + "logs_to_json": "standalone_tests/logs_to_json.py", + "json_to_html": "standalone_tests/json_to_html.py", + "json_output": "dtb_alignment.json", + "schema": "acs-results-schema.json#/definitions/raw_standalone_suite", + "detailed_html": "standalone_tests_detailed.html", + "summary_html": "standalone_tests_summary.html" + }, { "canonical": "ETHTOOL-TEST", "aliases": ["ETHTOOL-TEST", "ETHTOOLTEST", "ETHTOOL_TEST"], @@ -460,6 +490,18 @@ {"name": "log", "root": "results", "candidates": ["linux_tools/dt-validate-parser.log"], "required": true} ] }, + "RESERVED-MEMORY-MAP": { + "handler": "standalone_single", + "inputs": [ + {"name": "log", "root": "results", "candidates": ["linux_tools/reserved_memory_map_test.log"], "required": true} + ] + }, + "DTB-ALIGNMENT": { + "handler": "standalone_single", + "inputs": [ + {"name": "log", "root": "results", "candidates": ["linux_tools/dtb_alignment_test.log"], "required": true} + ] + }, "ETHTOOL-TEST": { "handler": "standalone_single", "inputs": [ diff --git a/common/log_parser/test_categoryDT.json b/common/log_parser/test_categoryDT.json index a55764d4..3f5e3b40 100644 --- a/common/log_parser/test_categoryDT.json +++ b/common/log_parser/test_categoryDT.json @@ -1004,5 +1004,43 @@ "FunctionID": 4, "Main Readiness Grouping": "runtime readiness" } - ] + ], + "catID: 55": [ + { + "Suite": "Standalone", + "Description": [ + "The Devicetree specification defines requirements for reserved memory regions to be represented consistently in the UEFI memory map.", + "The Reserved Memory Map Compliance Test validates static /reserved-memory regions against the required UEFI memory types.", + "Regions with the no-map property are validated against EfiReservedMemoryType, while other static reserved-memory regions are validated against EfiBootServicesData.", + "Memory Reservation Block entries are also validated against EfiReservedMemoryType.", + "https://github.com/ARM-software/arm-systemready/blob/main/SystemReady-devicetree-band/Yocto/meta-woden/recipes-acs/install-files/files/reserved_memory_map_checker.py" + ], + "Test Suite": "Devicetree Reserved Memory Validation", + "specName": "DT", + "rel Import. to main readiness": "Minor", + "Waivable": "yes", + "SRS scope": "Required", + "FunctionID": 1, + "Main Readiness Grouping": "boot readiness" + } +], + +"catID: 56": [ + { + "Suite": "Standalone", + "Description": [ + "The Devicetree specification requires the Devicetree blob to be located at an 8-byte-aligned address.", + "The DTB Alignment Test validates the DTB Table address reported by firmware in dmem.log.", + "The test verifies that the DTB address is present, non-zero, and aligned to an 8-byte boundary.", + "https://github.com/ARM-software/arm-systemready/blob/main/SystemReady-devicetree-band/Yocto/meta-woden/recipes-acs/install-files/files/dtb_alignment_test.py" + ], + "Test Suite": "Devicetree Blob Alignment Validation", + "specName": "DT", + "rel Import. to main readiness": "Minor", + "Waivable": "yes", + "SRS scope": "Required", + "FunctionID": 1, + "Main Readiness Grouping": "boot readiness" + } +] } diff --git a/common/tools/test_categoryDT-schema.json b/common/tools/test_categoryDT-schema.json index d09644ec..47fc5479 100644 --- a/common/tools/test_categoryDT-schema.json +++ b/common/tools/test_categoryDT-schema.json @@ -32,6 +32,8 @@ "Capsule Update", "Capsule update", "ConsoleSupportTest", + "Devicetree Blob Alignment Validation", + "Devicetree Reserved Memory Validation", "DTValidation", "DevicePathProcotols", "EBBR requirements", @@ -196,7 +198,9 @@ "catID: 51": { "$ref": "#/$defs/catID" }, "catID: 52": { "$ref": "#/$defs/catID" }, "catID: 53": { "$ref": "#/$defs/catID" }, - "catID: 54": { "$ref": "#/$defs/catID" } + "catID: 54": { "$ref": "#/$defs/catID" }, + "catID: 55": { "$ref": "#/$defs/catID" }, + "catID: 56": { "$ref": "#/$defs/catID" } }, "required": [ "catID: 1", @@ -252,7 +256,9 @@ "catID: 51", "catID: 52", "catID: 53", - "catID: 54" + "catID: 54", + "catID: 55", + "catID: 56" ], "additionalProperties": false }