215 lines
7.9 KiB
Python
215 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Host-only ownership model for hybrid BigApp/JIT mapping composition."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
MAX_EXECUTABLE_SEGMENTS = 8
|
|
MAX_EVENTS = 256
|
|
MAX_TICKS = 256
|
|
|
|
CREATE_CHILD = "CREATE_CHILD"
|
|
RESERVE_REGION = "RESERVE_REGION"
|
|
CREATE_MIRROR = "CREATE_MIRROR"
|
|
CREATE_JIT_MASTER = "CREATE_JIT_MASTER"
|
|
MAP_EXECUTABLE = "MAP_EXECUTABLE"
|
|
CREATE_JIT_ALIAS = "CREATE_JIT_ALIAS"
|
|
MAP_HOST_ALIAS = "MAP_HOST_ALIAS"
|
|
MAP_REMOTE_ALIAS = "MAP_REMOTE_ALIAS"
|
|
COPY_ALIAS = "COPY_ALIAS"
|
|
UNMAP_REMOTE_ALIAS = "UNMAP_REMOTE_ALIAS"
|
|
UNMAP_HOST_ALIAS = "UNMAP_HOST_ALIAS"
|
|
CLOSE_JIT_ALIAS = "CLOSE_JIT_ALIAS"
|
|
CLOSE_JIT_MASTER = "CLOSE_JIT_MASTER"
|
|
FINALIZE_IMAGE = "FINALIZE_IMAGE"
|
|
RELEASE_MIRROR = "RELEASE_MIRROR"
|
|
UNMAP_REGION = "UNMAP_REGION"
|
|
KILL_AND_REAP_CHILD = "KILL_AND_REAP_CHILD"
|
|
OK = "OK"
|
|
ERROR = "ERROR"
|
|
|
|
INDEXED = {
|
|
CREATE_JIT_MASTER, MAP_EXECUTABLE, CREATE_JIT_ALIAS, MAP_HOST_ALIAS,
|
|
MAP_REMOTE_ALIAS, COPY_ALIAS, UNMAP_REMOTE_ALIAS, UNMAP_HOST_ALIAS,
|
|
CLOSE_JIT_ALIAS, CLOSE_JIT_MASTER,
|
|
}
|
|
OPERATIONS = INDEXED | {
|
|
CREATE_CHILD, RESERVE_REGION, CREATE_MIRROR, FINALIZE_IMAGE,
|
|
RELEASE_MIRROR, UNMAP_REGION, KILL_AND_REAP_CHILD,
|
|
}
|
|
|
|
|
|
class CompositionError(RuntimeError):
|
|
"""The synthetic ownership transaction cannot close safely."""
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CompositionPlan:
|
|
executable_segments: int
|
|
|
|
def __post_init__(self) -> None:
|
|
if not isinstance(self.executable_segments, int) \
|
|
or isinstance(self.executable_segments, bool) \
|
|
or not 1 <= self.executable_segments <= MAX_EXECUTABLE_SEGMENTS:
|
|
raise CompositionError("executable segment count is invalid")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FakeEvent:
|
|
operation: str
|
|
result: str
|
|
segment: int = -1
|
|
ticks: int = 1
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.operation not in OPERATIONS or self.result not in {OK, ERROR}:
|
|
raise CompositionError("event operation/result is invalid")
|
|
if (self.operation in INDEXED) != (self.segment >= 0):
|
|
raise CompositionError("event segment binding is invalid")
|
|
if not isinstance(self.segment, int) or isinstance(self.segment, bool):
|
|
raise CompositionError("event segment is invalid")
|
|
if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \
|
|
or not 1 <= self.ticks <= MAX_TICKS:
|
|
raise CompositionError("event ticks are invalid")
|
|
|
|
|
|
class FakeFacade:
|
|
"""Exact fake script with no process, mapping, clock or device access."""
|
|
|
|
def __init__(self, events: tuple[FakeEvent, ...]) -> None:
|
|
if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_EVENTS \
|
|
or any(type(event) is not FakeEvent for event in events):
|
|
raise CompositionError("fake composition script is invalid")
|
|
self._events = list(events)
|
|
self.ticks = 0
|
|
|
|
def invoke(self, operation: str, segment: int = -1,
|
|
cleanup: bool = False) -> str:
|
|
if not self._events:
|
|
raise CompositionError("fake composition script is exhausted")
|
|
event = self._events.pop(0)
|
|
if event.operation != operation or event.segment != segment:
|
|
raise CompositionError("fake composition ordering differs")
|
|
if not cleanup and self.ticks + event.ticks > MAX_TICKS:
|
|
raise CompositionError("composition deadline precedes operation")
|
|
self.ticks += event.ticks
|
|
return event.result
|
|
|
|
@property
|
|
def remaining(self) -> int:
|
|
return len(self._events)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CompositionOutcome:
|
|
classification: str
|
|
success: bool
|
|
child_alive: bool
|
|
image_retained: bool
|
|
resources_open: int
|
|
fail_closed_termination: bool
|
|
target_action_performed: bool = False
|
|
firmware_behavior_proven: bool = False
|
|
|
|
|
|
def _require(facade: FakeFacade, operation: str, segment: int = -1) -> None:
|
|
if facade.invoke(operation, segment) != OK:
|
|
raise CompositionError(f"{operation} failed")
|
|
|
|
|
|
def run_composition(plan: CompositionPlan, facade: FakeFacade) -> CompositionOutcome:
|
|
"""Compose synthetic resources; cleanup failure requires child termination."""
|
|
if type(plan) is not CompositionPlan or type(facade) is not FakeFacade:
|
|
raise CompositionError("composition boundary is invalid")
|
|
child = region = mirror = committed = False
|
|
masters: set[int] = set()
|
|
aliases: set[int] = set()
|
|
host_aliases: set[int] = set()
|
|
remote_aliases: set[int] = set()
|
|
primary_failed = cleanup_failed = terminated = False
|
|
|
|
try:
|
|
_require(facade, CREATE_CHILD)
|
|
child = True
|
|
_require(facade, RESERVE_REGION)
|
|
region = True
|
|
_require(facade, CREATE_MIRROR)
|
|
mirror = True
|
|
for segment in range(plan.executable_segments):
|
|
_require(facade, CREATE_JIT_MASTER, segment)
|
|
masters.add(segment)
|
|
_require(facade, MAP_EXECUTABLE, segment)
|
|
_require(facade, CREATE_JIT_ALIAS, segment)
|
|
aliases.add(segment)
|
|
_require(facade, MAP_HOST_ALIAS, segment)
|
|
host_aliases.add(segment)
|
|
_require(facade, MAP_REMOTE_ALIAS, segment)
|
|
remote_aliases.add(segment)
|
|
_require(facade, COPY_ALIAS, segment)
|
|
_require(facade, UNMAP_REMOTE_ALIAS, segment)
|
|
remote_aliases.remove(segment)
|
|
_require(facade, UNMAP_HOST_ALIAS, segment)
|
|
host_aliases.remove(segment)
|
|
_require(facade, CLOSE_JIT_ALIAS, segment)
|
|
aliases.remove(segment)
|
|
_require(facade, CLOSE_JIT_MASTER, segment)
|
|
masters.remove(segment)
|
|
_require(facade, FINALIZE_IMAGE)
|
|
_require(facade, RELEASE_MIRROR)
|
|
mirror = False
|
|
committed = True
|
|
except Exception:
|
|
primary_failed = True
|
|
|
|
if primary_failed:
|
|
for resources, operation in (
|
|
(remote_aliases, UNMAP_REMOTE_ALIAS),
|
|
(host_aliases, UNMAP_HOST_ALIAS),
|
|
(aliases, CLOSE_JIT_ALIAS),
|
|
(masters, CLOSE_JIT_MASTER),
|
|
):
|
|
for segment in sorted(resources, reverse=True):
|
|
if facade.invoke(operation, segment, cleanup=True) != OK:
|
|
cleanup_failed = True
|
|
else:
|
|
resources.remove(segment)
|
|
if mirror:
|
|
if facade.invoke(RELEASE_MIRROR, cleanup=True) != OK:
|
|
cleanup_failed = True
|
|
else:
|
|
mirror = False
|
|
if region:
|
|
if facade.invoke(UNMAP_REGION, cleanup=True) != OK:
|
|
cleanup_failed = True
|
|
else:
|
|
region = False
|
|
if child:
|
|
if facade.invoke(KILL_AND_REAP_CHILD, cleanup=True) != OK:
|
|
raise CompositionError("fail-closed child termination failed")
|
|
child = False
|
|
terminated = True
|
|
masters.clear()
|
|
aliases.clear()
|
|
host_aliases.clear()
|
|
remote_aliases.clear()
|
|
mirror = region = False
|
|
|
|
if facade.remaining:
|
|
raise CompositionError("fake composition script has unused operations")
|
|
open_count = (int(mirror) + int(region) + len(masters) + len(aliases) +
|
|
len(host_aliases) + len(remote_aliases))
|
|
if primary_failed:
|
|
if child or open_count:
|
|
raise CompositionError("failed composition retained ownership")
|
|
classification = ("OFFLINE_FAIL_CLOSED_AFTER_CLEANUP_FAILURE" if cleanup_failed
|
|
else "OFFLINE_COMPOSITION_ROLLED_BACK")
|
|
return CompositionOutcome(classification, False, False, False, 0,
|
|
terminated)
|
|
if not committed or not child or not region or mirror or open_count != 1:
|
|
raise CompositionError("successful composition invariants failed")
|
|
return CompositionOutcome("OFFLINE_HYBRID_COMPOSITION_COMPLETE", True,
|
|
True, True, 0, False)
|