mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- Updated mailbox purpose tests to reflect new write order and sequence handling. - Added tests to ensure Linux-ready values match LP contract and constants match Linux helper. - Introduced tests to verify that offset changes do not hide stale Linux helpers. - Enhanced environment tests to report current locked launcher and runtime versions. - Added checks for SBT launcher presence in environment labels. - Improved full diagram tests to validate storage geometry and RSA vector configurations. - Expanded register tests to ensure crypto interrupt registers publish all events and lifecycle annotations preserve security boundaries. - Modified script tools tests to include Vexii generation validation and updated workflow assertions.
195 lines
9.4 KiB
Python
195 lines
9.4 KiB
Python
"""Prevent publication drift in HP acceptance and ready-only Linux integration."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from publications import boot_acceptance_reference as br
|
|
from publications.software_reference import function_body
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
@pytest.fixture
|
|
def source_tree(tmp_path):
|
|
for relative in br.SOURCES:
|
|
target = tmp_path / relative
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copyfile(ROOT / relative, target)
|
|
return tmp_path
|
|
|
|
|
|
def replace(root: Path, relative: str, old: str, new: str) -> None:
|
|
path = root / relative
|
|
text = path.read_text(encoding="utf-8")
|
|
assert old in text
|
|
path.write_text(text.replace(old, new), encoding="utf-8")
|
|
|
|
|
|
def test_actual_boot_requires_bounded_complete_acceptance():
|
|
result = br.collect_boot_acceptance(ROOT, function_body)
|
|
assert result["event_poll_iterations"] == 64000000
|
|
assert result["npu_defines"] == ["RS_NPU_P5_ACCEPTANCE", "RS_NPU_P6_ACCEPTANCE"]
|
|
assert [(row["event"], row["argument"], row["sequence"]) for row in result["messages"]] == [
|
|
(1, 0x4C4E5801, 1), (2, 0x47413244, 2), (4, 0x43424F4B, 3)]
|
|
assert result["rootfs_ready_only"]
|
|
assert result["terminal_requires_cache_handoff"]
|
|
assert result["failure_reset_requires_no_hp_ga2d_owner"]
|
|
|
|
|
|
def test_mailbox_purposes_follow_registers_in_the_actual_write_order():
|
|
rows = br.ready_mailbox(ROOT)
|
|
assert [(row["address"], row["register"], row["purpose"]) for row in rows] == [
|
|
("0x10019020", "HP_EVENT", "Linux-ready event"),
|
|
("0x10019024", "HP_ARG0", "Ready-state argument"),
|
|
("0x10019028", "HP_SEQUENCE", "Publication sequence"),
|
|
("0x1001902C", "HP_DOORBELL", "LP interrupt request"),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("relative,old,new,match", [
|
|
(br.HAL_SOURCE, "RS_HP_MAILBOX_HP_EVENT_OFFSET UINT32_C(0x020)",
|
|
"RS_HP_MAILBOX_HP_EVENT_OFFSET UINT32_C(0x024)", "HAL/RTL"),
|
|
(br.HAL_SOURCE, "message->code = *rs_hp_mailbox_register(RS_HP_MAILBOX_HP_EVENT_OFFSET);",
|
|
"message->code = *rs_hp_mailbox_register(RS_HP_MAILBOX_HP_ARG0_OFFSET);", "field semantics"),
|
|
(br.MAILBOX_SOURCE, "`APB4_HP_MAILBOX__HP_DOORBELL: s_lp_intr_state_d = 1'b1;",
|
|
"`APB4_HP_MAILBOX__HP_DOORBELL: s_hp_intr_state_d = 1'b1;", "LP interrupt"),
|
|
(br.READY_SOURCE, "for (;;) {", "return 0;\n for (;;) {", "ready-only"),
|
|
(br.READY_SOURCE, "mailbox[11] = 1U;", "mailbox[8] = 2U;\n mailbox[11] = 1U;", "sequence"),
|
|
])
|
|
def test_mailbox_or_service_drift_requires_new_review(source_tree, relative, old, new, match):
|
|
replace(source_tree, relative, old, new)
|
|
with pytest.raises(ValueError, match=match):
|
|
br.ready_mailbox(source_tree)
|
|
|
|
|
|
@pytest.mark.parametrize("old,new", [
|
|
("mailbox[8] = (error == 0U) ? 1U : 3U;", "mailbox[8] = (error == 0U) ? 2U : 3U;"),
|
|
("UINT32_C(0x4C4E5801) : error", "UINT32_C(0x4C4E5802) : error"),
|
|
("mailbox[10] = 1U;", "mailbox[10] = 2U;"),
|
|
("mailbox[11] = 1U;", "mailbox[11] = 2U;"),
|
|
])
|
|
def test_each_linux_ready_value_must_match_the_lp_contract(source_tree, old, new):
|
|
replace(source_tree, br.READY_SOURCE, old, new)
|
|
with pytest.raises(ValueError, match="values differ"):
|
|
br.ready_mailbox(source_tree)
|
|
|
|
|
|
@pytest.mark.parametrize("old,new", [
|
|
("RS_HP_BOOT_READY_EVENT UINT32_C(1)",
|
|
"RS_HP_BOOT_READY_EVENT UINT32_C(2)"),
|
|
("RS_HP_BOOT_READY_ARG UINT32_C(0x4C4E5801)",
|
|
"RS_HP_BOOT_READY_ARG UINT32_C(0x4C4E5802)"),
|
|
("RS_HP_BOOT_MAILBOX_READY_SEQUENCE UINT32_C(1)",
|
|
"RS_HP_BOOT_MAILBOX_READY_SEQUENCE UINT32_C(2)"),
|
|
])
|
|
def test_each_lp_ready_constant_must_match_the_linux_helper(source_tree, old, new):
|
|
replace(source_tree, br.BOOT_SOURCE, old, new)
|
|
with pytest.raises(ValueError, match="values differ"):
|
|
br.ready_mailbox(source_tree)
|
|
|
|
|
|
def test_coordinated_hal_rtl_offset_change_cannot_hide_a_stale_linux_helper(source_tree):
|
|
replace(source_tree, br.HAL_SOURCE,
|
|
"RS_HP_MAILBOX_HP_EVENT_OFFSET UINT32_C(0x020)",
|
|
"RS_HP_MAILBOX_HP_EVENT_OFFSET UINT32_C(0x024)")
|
|
replace(source_tree, br.REGISTER_SOURCE,
|
|
"`define APB4_HP_MAILBOX__HP_EVENT 12'h020",
|
|
"`define APB4_HP_MAILBOX__HP_EVENT 12'h024")
|
|
with pytest.raises(ValueError, match="addresses differ"):
|
|
br.ready_mailbox(source_tree)
|
|
|
|
|
|
def test_rtl_doorbell_offset_change_cannot_hide_a_stale_linux_helper(source_tree):
|
|
replace(source_tree, br.REGISTER_SOURCE,
|
|
"`define APB4_HP_MAILBOX__HP_DOORBELL 12'h02C",
|
|
"`define APB4_HP_MAILBOX__HP_DOORBELL 12'h050")
|
|
with pytest.raises(ValueError, match="addresses differ"):
|
|
br.ready_mailbox(source_tree)
|
|
|
|
|
|
@pytest.mark.parametrize("old,new,match", [
|
|
("timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout", "; ++timeout", "bounded poll"),
|
|
("if (status == 1U)", "if (status != 1U)", "token/error"),
|
|
("if (!s_hp_boot_ga2d_owned_by_hp)", "if (s_hp_boot_ga2d_owned_by_hp)", "reset ownership"),
|
|
("rs_hp_boot_fail(UINT8_C(13));", "rs_hp_boot_fail(UINT8_C(11));", "completion condition"),
|
|
("cache_clean_completed = true;", "cache_clean_completed = false;", "phase or ordering"),
|
|
("rs_resource_set_owner(RS_RESOURCE_GA2D, RS_RESOURCE_OWNER_LP, false)",
|
|
"rs_resource_set_owner(RS_RESOURCE_GA2D, RS_RESOURCE_OWNER_HP, false)", "phase or ordering"),
|
|
("#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)",
|
|
"#if 1", "optional NPU"),
|
|
])
|
|
def test_inaccurate_boot_protocol_claims_fail_collection(source_tree, old, new, match):
|
|
replace(source_tree, br.BOOT_SOURCE, old, new)
|
|
with pytest.raises(ValueError, match=match):
|
|
br.collect_boot_acceptance(source_tree, function_body)
|
|
|
|
|
|
def test_ready_marker_cannot_become_terminal_success_unnoticed(source_tree):
|
|
replace(source_tree, br.BOOT_SOURCE, 'printf("HP_LINUX_READY\\n");',
|
|
'printf("HP_LINUX_READY\\n");\n rs_test_finish(RS_TEST_PASSED, UINT8_C(0));')
|
|
with pytest.raises(ValueError, match="terminal pass"):
|
|
br.collect_boot_acceptance(source_tree, function_body)
|
|
|
|
|
|
@pytest.mark.parametrize("old,new", [
|
|
("if (rs_hp_boot_wait_hp_held())", "if (!rs_hp_boot_wait_hp_held())"),
|
|
("rs_resource_acknowledge_cache_clean() == RS_OK", "rs_resource_acknowledge_cache_clean() != RS_OK"),
|
|
("if (rs_hp_boot_wait_cache_request())", "if (!rs_hp_boot_wait_cache_request())"),
|
|
("if (rs_hp_boot_wait_message(RS_HP_BOOT_GA2D_CACHE_EVENT,", "if (!rs_hp_boot_wait_message(RS_HP_BOOT_GA2D_CACHE_EVENT,"),
|
|
])
|
|
def test_inverted_cache_success_guard_cannot_qualify_handoff(source_tree, old, new):
|
|
replace(source_tree, br.BOOT_SOURCE, old, new)
|
|
with pytest.raises(ValueError, match="positive cache-handoff"):
|
|
br.collect_boot_acceptance(source_tree, function_body)
|
|
|
|
|
|
@pytest.mark.parametrize("function,old,new,match", [
|
|
("rs_hp_boot_wait_hp_held", "&& hp_status.reset_asserted", "", "held-state"),
|
|
("rs_hp_boot_wait_hp_held", "!hp_status.released", "hp_status.released", "held-state"),
|
|
("rs_hp_boot_wait_hp_held", "!hp_status.draining", "hp_status.draining", "held-state"),
|
|
("rs_hp_boot_wait_hp_held", "!hp_status.forced_fault &&", "", "held-state"),
|
|
("rs_hp_boot_wait_hp_held", "rs_hp_boot_ga2d_idle(RS_RESOURCE_OWNER_HP)",
|
|
"rs_hp_boot_ga2d_idle(RS_RESOURCE_OWNER_LP)", "held-state"),
|
|
("rs_hp_boot_wait_hp_held", "|| defined(RS_NPU_P6_ACCEPTANCE)", "", "held-state NPU"),
|
|
("rs_hp_boot_wait_hp_held", "&& rs_hp_boot_npu_idle(RS_RESOURCE_OWNER_HP)",
|
|
"|| rs_hp_boot_npu_idle(RS_RESOURCE_OWNER_HP)", "held-state NPU"),
|
|
("rs_hp_boot_wait_cache_request", "cache_status.request && !cache_status.clean",
|
|
"cache_status.request && cache_status.clean", "cache request/clean"),
|
|
("rs_hp_boot_ga2d_idle", "!ga2d_status.draining &&", "", "ga2d safe-idle"),
|
|
("rs_hp_boot_npu_idle", "RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING | RS_NPU_STATUS_RECOVERING",
|
|
"RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING", "npu safe-idle"),
|
|
])
|
|
def test_weakened_held_observation_cannot_qualify_handoff(source_tree, function, old, new, match):
|
|
path = source_tree / br.BOOT_SOURCE
|
|
source = path.read_text(encoding="utf-8")
|
|
body = function_body(source, function)
|
|
assert old in body
|
|
path.write_text(source.replace(body, body.replace(old, new), 1), encoding="utf-8")
|
|
with pytest.raises(ValueError, match=match):
|
|
br.collect_boot_acceptance(source_tree, function_body)
|
|
|
|
|
|
def test_poll_budget_is_derived_without_inventing_time_units(source_tree):
|
|
replace(source_tree, br.STATUS_SOURCE, "((rs_timeout_t)1000000U)", "((rs_timeout_t)2000000U)")
|
|
result = br.collect_boot_acceptance(source_tree, function_body)
|
|
assert result["default_poll_iterations"] == 2000000
|
|
assert result["event_poll_iterations"] == 128000000
|
|
assert not any("milliseconds" in key or "seconds" in key for key in result)
|
|
|
|
|
|
def test_mailbox_address_is_derived_from_the_memory_map(source_tree):
|
|
path = source_tree / br.MAP_SOURCE
|
|
source = json.loads(path.read_text(encoding="utf-8"))
|
|
mailbox = next(row for row in source["regions"] if row["symbol"] == "APB4_HP_MAILBOX")
|
|
mailbox["base"] = "0x1001A000"
|
|
path.write_text(json.dumps(source), encoding="utf-8")
|
|
replace(source_tree, br.READY_SOURCE, "0x10019000", "0x1001A000")
|
|
rows = br.ready_mailbox(source_tree)
|
|
assert rows[0]["address"] == "0x1001A020"
|
|
assert rows[0]["purpose"] == "Linux-ready event"
|