config: Build the chain and the kinds from the device table - #526
Merged
Merged
Conversation
chrysh
force-pushed
the
one-device-table
branch
2 times, most recently
from
October 1, 2026 21:02
72c60b1 to
87969a7
Compare
ComponentKind was written out twice: once in ComponentAttrs on the orchestrator's chain, once in Board::component_kinds. Nothing checked they matched. Disagree and the machine waits in AwaitingReady for a readiness the driver never sends, the boot watchdog fires, and the component goes round the recovery loop until its retries run out. DeviceConfig now carries the ComponentAttrs for its device, and chain_of turns a table into the chain the machine runs on. Board no longer holds the kinds at all: the driver reads them off the chain entries it was built with, so the second copy is gone rather than hidden. Holding ComponentAttrs whole rather than copying its four fields keeps one definition of what a component's policy is. A field added there reaches the table with no change here. chain_of returns a ChainEntries newtype with a private field. The only way to get one is through chain_of, and boards declare it as a const item, so the validation (nonempty, at most u8::MAX, no forward or self deps) runs at compile time. A bad table is a build error, never a runtime panic. bring_up takes &'static ChainEntries and is infallible: the const fence already guarantees everything Chain::try_from would check, so the conversion uses an expect rather than returning a Result. The old bring_up_reports_a_chain_the_machine_refuses test is gone because the scenario is now unrepresentable. Table order assigns the ids: devices[i] is ComponentId(i), which is also how the driver indexes every per-component array. A table that came through chain_of is in position by construction; the check in PlatformDriver::new catches synthetic entries in the driver's own tests. chain_of also checks the depends_on links while it has the whole table, because a dependency the chain does not contain would silently never cascade. Orchestrator::new and Chain::try_from stay public: the state machine's own tests and the ast10x0 runtime harness build synthetic chains, and narrowing that to win an encapsulation point would cost more than it buys. config gains a dependency on sm for the vocabulary. driver gains a dependency on config for ChainEntries. Assisted-by: Claude
chrysh
force-pushed
the
one-device-table
branch
from
October 3, 2026 19:25
87969a7 to
19963f1
Compare
chrysh
marked this pull request as ready for review
October 3, 2026 19:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #375.
ComponentKindwas written out twice: once inComponentAttrson the orchestrator's chain, once inBoard::component_kinds. Nothing checked they matched. Disagree and the machine waits inAwaitingReadyfor a readiness the driver never sends, the boot watchdog fires, and the component goes round the recovery loop until its retries run out.DeviceConfignow carries theComponentAttrsfor its device, andchain_ofturns a table into the chain the machine runs on.Boardno longer holds the kinds at all: the driver reads them off the chain entries it was built with, so the second copy is gone rather than hidden.It holds
ComponentAttrswhole rather than copying its four fields, so there is one definition of what a component's policy is and a field added there reaches the table with no change inconfig.bring_uptakes the entries once and returns both halves, and it is the only way to a driver from outside the crate. A board cannot hand the two sides different lists because it only passes one.Table order assigns the ids:
devices[i]isComponentId(i), which is also how the driver indexes every per-component array.chain_ofchecks thedepends_onlinks while it has the whole table, because a dependency the chain does not contain is dropped at the dispatch boundary and would silently never cascade, and it bounds the table at whatChaintakes.configgains a dependency onsmfor the vocabulary. The other direction would put a dependency on the decision core, which stays free of them.Every existing table now states its kinds, which meant deciding them: the mock NIC is
active_requiredbecause it is SPDM-capable and has an iRoT, the mock BMC and the ast1060 BMC arepassive_requiredbecause they have none.Where the guarantee stops
Orchestrator::newandChain::try_fromstay public. The state machine's own tests and the ast10x0 runtime harness build synthetic chains of varying size, and narrowing that to win an encapsulation point would cost more than it buys. So going aroundbring_upis possible, but it is visible: there is no longer an API through which the two lists can quietly disagree.The position rule is checked in two places for two reasons. A table that came through
chain_ofis in position by construction, at build time. The assert inPlatformDriver::newcatches hand-built entries at bring-up, before anything leaves reset, because the driver indexes every per-component array byid.get()and an entry out of position would address the wrong component's reset line, flash and floor.One correspondence stays hand-authored:
Board's per-component arrays. A hardware handle carries no identity, so there is nothing to derive "board.images[1]belongs to the BMC" from, and nothing to check it against. A board that composes its arrays in a different order than its table still boots the wrong device, andbring_upcannot catch it. That is tracked as #529; it is a different problem from two lists of devices disagreeing, which is what this closes.