Repository navigation
uci: unfence UCI register reads in the 6510 products (wiki 375->175 s onchip, 364->163 s comb on U64E); UCI_READ_FENCE=1 restores them - #299
Conversation
…ores them The plain UCI adapter put a uci_fence after every register access. Reads need none (the queues advance on the read, DATA_AV/STAT_AV are polled before every byte); measured on the U64E with the fenced 6510 comb build, read fences were 200 s of 363.6 s on the 751,823 B wiki page (#296's fairness measurement). Every WRITE fence stays, and so do the push rule, the shared uci_fence macro and every TOD-bounded wait. The 15 read sites in net.s (5) and uci_cmd.s (10) now use uci_read_fence (or an .ifdef around their jsr uci_settle). The trust store's uci_dos.s reads are left fenced: that path cannot be exercised on the C64U (no SD card), so it is not changed here. UCI_READ_FENCE=1 (BACKEND=uci only, in flags.stamp) assembles the previous images byte for byte: uci-onchip 375f9480, uci-comb 74a7d358, plain uci b4e4e6fb. uci-m3 (44648860) and ip65 are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
tools/check_uci_read_pacing.py runs the shipped adapter (net_init, connect, data / idle / EOF / reset polls, send, close) on test_uci_abort_recovery's interpreter and model, and logs every $DF1C-$DF1F access with the uci_fence executions after it: every write must be fenced; on the default image no read is, except the fence net_poll keeps before its header read (now labelled uci_poll_stage_fence; a label, no bytes); on a UCI_READ_FENCE=1 image every read is. --expect flips the expectation to show it red on the other image. tools/mutate_uci_read_pacing.sh: 5 source mutants (two write fences dropped, three read fences re-added); all killed, control 6/6. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
7215316 to
fa685cb
Compare
…nts PRG sha256 adv-299 finding 1: uci_dos.s keeps the fence after its own three reads, but every DOS transaction also runs through the primitives this branch unfenced (uci_wait_idle, uci_wait_reply, uci_check_err, uci_drain_resp, uci_drain_status). So on the default image the trust store's traffic (trust_pre on every 'G' in both shipped products) has unfenced reads too. check_uci_read_pacing.py now runs the store's DOS traffic on a TRUST_STORE=1 image (load on an empty medium, stage + save, reload; on test_trust_store_6502.py's DOS model) and holds it to the same rule: no read fenced on the default image except uci_dos.s's own three, which are labelled (uci_dos_rd_part / _data / _state; labels, no bytes) and must stay fenced on every image. mutate_uci_read_pacing.sh builds the onchip store image and adds dos-own-unfenced and check-err-fenced: 7/7 killed. rig_https_local.py prints the sha256 of the PRG bytes it loads (adv-299 finding 3: a rebuild of build/ during a run must not be mistakable for what ran). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dos count adv-299 round 2 (low): no flow executed the data read at uci_cmd.s @drn_have, so re-fencing it on the default image or unfencing it on a UCI_READ_FENCE=1 image went unseen. New flow "poll overclaim": a 600 B header over a 512 B request, so the copy stops at the request and uci_drain_resp reads the rest. mutate_uci_read_pacing.sh adds drain-resp-fenced (killed: 49 fenced reads in that flow); the reverse, the site unfenced on the fenced image, is red too (49 unfenced reads). The docstring said uci_dos.s keeps four fenced reads; it is three (DOS_OWN). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Post-merge confirmation on master 1. 1 MHz, onchip, local listener (
This hash-logged fenced run replaces the pre-window 1 MHz fenced row, which was withdrawn from the PR. 2. Trust-store path on the U64E's
All 8 runs passed. 🤖 Generated with Claude Code |
What
BACKEND=uci(the shipped 6510 TLS products) put auci_fenceafter every UCI register access, reads included. This PR removes the fence after reads of$DF1C-$DF1Finsrc/net/uci: 15 sites, 5 innet.sand 10 inuci_cmd.s. They now useuci_read_fence, or an.ifdefaround theirjsr uci_settle. It is the same change #296 made for uci-m3.Unchanged:
uci_fencemacro and every TOD-bounded wait.net_pollkeeps before its header read. It is now labelleduci_poll_stage_fence; the label adds no bytes.uci_dos.s) is affected too, corrected after review (adv-299 finding 1).uci_dos.skeeps the fence after its own three reads, now labelleduci_dos_rd_part,_dataand_state(labels, no bytes).uci_wait_idle,uci_wait_reply,uci_check_err,uci_drain_respanduci_drain_status. So the store's traffic, whichtrust_preruns on every 'G' in both shipped products, has unfenced reads on the default image, and new read→write adjacencies.The stage fence and the DOS path are consistent in effect. After
uci_check_err's status read:net_pollrunsuci_poll_stage_fence, then reads$DF1Cand the data.dos_partreads$DF1Cand then fences (its ownjsr uci_settle) before the first data read.On both paths exactly one fence sits between the error check and the first response-data read. Whether either fence is needed was not tested; both are kept as they were on master. The stage fence is the only fence after a read left on the network path, and the checker exempts it by label.
Knob and hashes (rebased on master a7bccc9; clean builds)
UCI_READ_FENCE=1(BACKEND=uci only, refused elsewhere, recorded inflags.stamp) assembles master byte for byte:UCI_READ_FENCE=1USE_NISTCURVES_ONCHIP=1 TRUST_STORE=1)fd37600bfd37600bffa47fc3USE_NISTCURVES_ONCHIP_COMB=1 TRUST_STORE=1)025af934025af93467cb0048BACKEND=uci806fda4c806fda4cac4272736a1f52bd6a1f52bd924132deaa340d8faa340d8f697f18e181d0abaf81d0abaf24415efb94f0f3a494f0f3a467f9d2d4810f1158810f1158(unchanged)c64267c4c64267c4(unchanged)The REU-sink builds are
HTTPS_BODY_TO_REU=1 HTTPS_HOST=en.wikipedia.org HTTPS_PATH=/wiki/Commodore_64.Model + mutation (red/green, at this head)
tools/check_uci_read_pacing.py(new, standalone):test_uci_abort_recovery.py's interpreter and model through these flows:net_init; connect; data, idle, EOF and reset polls; an over-claimed header, which reachesuci_drain_resp's data read; send; close.$DF1C-$DF1Faccess together with theuci_fenceexecutions after it.uci_poll_stage_fence. On aUCI_READ_FENCE=1image every read is fenced.TRUST_STORE=1image it also runs the store's DOS traffic ontest_trust_store_6502.py's DOS model: a load on an empty medium, a stage + save, then a reload.uci_dos.s's own three reads must stay fenced on every image.ffa47fc3(onchip) and67cb0048(comb): 9/9;UCI_READ_FENCE=1fd37600b: 9/9 (at 78e5aea);ac427273/806fda4c: no store, so the trust flows are not applicable;--expect), it fails both ways.tools/mutate_uci_read_pacing.sh(builds the onchip store image): 8/8 killed, control 9/9.The reverse direction at that site is red too: with
uci_drain_resp's data read unfenced on aUCI_READ_FENCE=1image, the overclaim flow reports 49 unfenced reads (8/9).Suites on default
BACKEND=uci:pytest: 483 passed. The 1 failure istest_ip65_hw_checks_unit, which needs an ip65 build inbuild/; it fails the same way with master's bytes.test_uci_abort_recovery25/25,test_uci_resp_read_wait6/6,test_uci_dhcp_probe6/6.test_trust_store_6502128/128 on the onchip store image.test_runner_coverageandtest_pytest_boundarygreen.What the detector cannot see is in its docstring: a delay that is not a
uci_fence, and real FPGA timing. The hardware below covers timing.Hardware
Protocol for every run:
prepare_device, thenpreflight_reu.net_last_error,tcp_recv_overfloware logged on every attempt.C64 Ultimate: C64U fw 1.1.0 (stock), FPGA 122, core 1.49, at 10.43.23.26, 64 MHz. Built from this rebased tree (a7bccc9 base).
Prep set REU Enabled 16 MB and turbo 64 Manual before the reset. Wiki and robots.txt to REU, n=3 per cell, median (min-max) s:
/robots.txt28,275 B81d0abaf/24415efb)/robots.txt94f0f3a4/67f9d2d4)/wiki/Commodore_64751,822 B/wiki/Commodore_64net_last_error $00,tcp_recv_overflow0, and complete by the client's framing.79C6EE37).373614AA(751,822 B), metadata-only against its host reference. One comb-fenced attempt was served chunked and is complete by its terminal chunk, with the same bytes.writememthat types 'Q' at the viewer often returned "connection dropped" (15 of 24). The key had landed every time: "CONNECTION CLOSED" was on screen within 60 s, and no socket was left open.Local listener (
rig_https_local.py, 64 MHz,HTTPS_SNI=www.foo.invalidno-store builds,DEBUG_CAPTURE=0, FUUFFU):a665e6f9fenced /509a247cunfenced): 6/6 PASS,$00. Sentinel at 40.7-41.3 s fenced, 40.0-41.0 s unfenced.6e4c6c51/ef103660): 6/6 PASS,$00. Sentinel at 32.8-33.3 s fenced, 32.2-32.9 s unfenced.Ultimate64DeviceAliasErroronrun_prg. My wrapper also held the legacy raw-address lockfile for .26, and the harness read that as a second lock holder. The runs above were made without that wrapper.C64U, store path and small-transaction stress (adv-299 findings 1 and 2; tree cdc35a3, i.e. this head's code; 64 MHz; one DeviceLock per attempt; F/U interleaved):
Flash, SD, Temp, writableSD. The C64U now has an SD card.rig_trust_store.py, store at/SD/c64https-test, every check per run (24 on onchip, 25 on comb: the 25th, "cold bank: the image is in cert_buf", exists only there): load EMPTY, save, VALID, ping-pong, a torn write plus reset, FORMAT fallback, repair, CHECKSUM, VERSION, 32 records (a 2,064 B image: five 512 B read parts, nine write chunks), FULL, NOPATH.c9af5616and unfenced74d9c623: 24/24 PASS each.b0bb6289and fencedc11c09ff: 25/25 PASS each.rig_https_banner.py, typeden.wikipedia.org /robots.txt):fd37600b×2 and unfencedffa47fc3×2; comb unfenced67cb0048×1 and fenced025af934×1./USB1, which the C64U does not have. So on every run, fenced and unfenced,trust_pre's DOS traffic runs and fails cleanly with the same outcome:TRUST FAIL 02(R_NOPATH), the "CONTINUE UNPINNED? Y/N" prompt, then "NOT DIALLED" after the rig's 'Q'.rig_trust_policy.pyLIVE (comb, wiki, store on/SD; fenced30d3c2cf, unfenced84f89fda): both reached "EMPTY + NEW HOST" with "CONNECTION CLOSED" on screen. Both then died in the rig on the same harness error: the 'Q'writemem"connection dropped", which this stock firmware does often (below). Inconclusive for the policy's later phases, on both arms alike.94f0f3a4/67f9d2d4), so 9 per arm with the A/B above.$00.net_last_errorwas$00on every fetch, and no$86/$88/$89appeared.U64E: 3.15 git
3a1ff9ff, FPGA 126, core 1.50. Built from the pre-rebase tree (71b047e base + this PR's two commits).Why that is still a valid measurement of this change:
git diff 71b047e a7bccc9 -- src/net/is empty.http.s/boot.sedits. But the A/B arms differ only insrc/net/uciplus the knob, on both bases.48 MHz, same protocol, n=3 per cell:
/robots.txt5d8f1447/7204e616)/robots.txtf6d67a27/a980b21d)/wiki/Commodore_64751,823 B/wiki/Commodore_64$00, overflow 0, and complete by framing.9C298DA4, metadata-only against the host reference. The 12th (onchip fenced, 07:23:36) was a 752,043 B experiment render, served chunked and complete by its terminal chunk. It is DIFFERENT only against the host's cached render.Local listener on the U64E (
HTTPS_SNI=www.foo.invalidbuilds,DEBUG_CAPTURE=0, 71b047e base):f737e13a/cab45227): 6/6 PASS.5c03dee8/6b6e0390): 6/6 PASS.cab45227) PASS, sentinel at 2,235.3 s,$00.build/while it was in flight. The rig loadsbuild/by path and, at the time, logged no hash of what it loaded, and the labels are identical across arms, so what ran cannot be proven.rig_https_local.pynow prints the sha256 of the PRG bytes it actually loads. A 1 MHz rerun can follow after the U64E's firmware window.Not covered
/USB1. That was run, and it is clean and identical across arms; the store itself was exercised with/SDbuilds instead./USB1) waits for the end of the U64E firmware window.rig_https_localcannot pass on any store build: its trampoline never runstrust_pre, so the store refuses the certificate ($04), on master too. The local-listener runs therefore use no-store builds, which differ from the products only inTRUST_STORE.$DF1Cbit 3: there is no in-PRG counter on this backend. The evidence rests onnet_last_error, where a refused push lands as$86: it was$00on every fetch. (My scratch rig also logged the UCI status line, but it over-read the 16 Buci_status_buf, so those logs are not evidence and are not cited.)🤖 Generated with Claude Code