Repository navigation
Conversation
Review: #1259 at c991bfd (round 1)Architecture. Step 1 conforms to design rev 4.
W^X and COW (verified).
ROOT's pages withdrawn while mapped. This cannot happen in step 1, by construction:
Until step 2's revocation exists, step 1 must keep one guarantee: Deleted The two fixed defects.
Overlaps (checked with
RequiredNone. Suggestions
Evidence, and what the pending runs must showHost evidence holds.
No boot has run this head yet. Every boot now spawns the supervisor through the new shared path. Before landing:
Not a landing precondition: the used memory of 255 held children. The issue record carries it as owed, and this PR claims no figure. LAND, pending the guest and metal results above. |
…t step 1 keeps until step 2 - `loader::Map::rights` says what it answers: the rights the handle naming the range's object must carry. - `SpawnMap`'s doc names `PermissionDenied` again: a handle without `MAP`, or without `WRITE` beside it for a writable share. - The memory track record names the exit for the lost `mprotect` widening of a shared segment: step 2's convert-on-protect. - The record says what step 1 relies on until step 2's revocation exists: an object a space maps keeps its frames for its life, so ROOT's pages stay non-reclaimable and ROOT is not served through a pager until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Response to review r1 (head 3baf239)3baf239 is c991bfd plus comments, one doc sentence and the record: no behaviour changes, so the staged images and guest run at c991bfd stand for it.
|
|
Orchestrator: verified 3baf239 against the review's optional items. |
…t step 1 keeps until step 2 - `loader::Map::rights` says what it answers: the rights the handle naming the range's object must carry. - `SpawnMap`'s doc names `PermissionDenied` again: a handle without `MAP`, or without `WRITE` beside it for a writable share. - The memory track record names the exit for the lost `mprotect` widening of a shared segment: step 2's convert-on-protect. - The record says what step 1 relies on until step 2's revocation exists: an object a space maps keeps its frames for its life, so ROOT's pages stay non-reclaimable and ROOT is not served through a pager until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Guest red in run 43062438931 ( The test read its own peak memory through The fix measures in children: three runs of the program read nothing, the read-only table and the writable table, and the spawner reads each one's peak off its end through the handle its spawn answers (
|
…res as SHM_CREATE's The first-touch record states the orchestrator's decision as made: one draw, `pmm::alloc_promised`, for a region's hole (`SharedMemObject::page()`, into the region's slot) and a MEMORY volume's hole (into the pager's index under its lock), two stores because one is revoked and the other never; and an inbox's rings stay drawn whole beside a device's run, since the kernel writes them in interrupt context, in the decision and its exit. The build plan (on #1259) leaves the decision. Its figures are `SHM_CREATE`'s, and that those are the windows is labelled an inference. Every record links the evidence whose script sorts deterministically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Guest run 43081676127 at 470bf6f is green: |
Review: #1259's test fix, at ae99d1d (commits 470bf6f, ae99d1d)Scope. 470bf6f changes only Diagnosis: correct. The loader's Claims: unchanged, and the measurement holds.
Evidence.
Record (ae99d1d). Accurate, and it follows the benches-down ruling. It names the rows, both benches and what main must show. The FindingsBLOCKER
let mut reader = Command::new(SELF_PATH).arg(role).spawn().expect("vm_map_shares: a reader starts");
let status = reader.wait().expect("vm_map_shares: wait a reader");
assert_eq!(End::from_word(status.into_raw()), Some(End::Exited(0)), "vm_map_shares: the {role} reader");
let mut stats = ProcessStats::default();
syscall::process_stats(RawHandle(reader.as_raw_handle()), &mut stats).expect("vm_map_shares: a reader's statistics");
stats.peak_memoryThis removes about 14 lines, and a failure then names its cause. It needs one more SUGGESTION SEND BACK |
|
Review r2 answered at 3970ec0.
Guest: run 43093948691 at 3970ec0, |
Follow-up review: #1259 at 3970ec0 (since ae99d1d)Scope. This round adds one commit, 3970ec0, whose parent is ae99d1d. It changes only The previous blocker is resolved. It compiles, by inspection.
The handle and its rights are unchanged.
The test's claims are unchanged.
The adopted comment is accurate. Evidence. Guest run 43093948691 ( FindingsNone. LAND, once run 43093948691 is green. |
… lock Two touches of a hole both drew before racing to install, so the loser drew against a promise its page did not hold, and the machine's promised count fell short of the promises outstanding until it gave the frame back. Each region now has a first-touch lock (`sync::Lock`): a touch takes an installed frame with one Acquire load, and a hole is drawn by the touch that, holding the lock, still finds it a hole, and installed with a Release store. Every draw is the page's own promise's; `discard` keeps #1250's caller and Safety text. The lock order is space, region, allocator, and nothing else takes the region's lock. The loom model asserts one draw a page, and `slot-install-relaxed` still reds the publication. The batched draw of an object run's holes is recorded without condition, owed by the object fault-around (#1259): whichever of #1259 and this lands second draws a run's holes in one hold. The free batch is a superframe's worth by name, and the promise doc is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t step 1 keeps until step 2 - `loader::Map::rights` says what it answers: the rights the handle naming the range's object must carry. - `SpawnMap`'s doc names `PermissionDenied` again: a handle without `MAP`, or without `WRITE` beside it for a writable share. - The memory track record names the exit for the lost `mprotect` widening of a shared segment: step 2's convert-on-protect. - The record says what step 1 relies on until step 2's revocation exists: an object a space maps keeps its frames for its life, so ROOT's pages stay non-reclaimable and ROOT is not served through a pager until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Rebased onto main 06fcb81 (#1246, #1226, #1216 among what landed); head 1f8a0e4. Four conflicts, each resolved so both sides hold:
One more site merged cleanly but no longer compiled: Checks at 1f8a0e4: |
… lock Two touches of a hole both drew before racing to install, so the loser drew against a promise its page did not hold, and the machine's promised count fell short of the promises outstanding until it gave the frame back. Each region now has a first-touch lock (`sync::Lock`): a touch takes an installed frame with one Acquire load, and a hole is drawn by the touch that, holding the lock, still finds it a hole, and installed with a Release store. Every draw is the page's own promise's; `discard` keeps #1250's caller and Safety text. The lock order is space, region, allocator, and nothing else takes the region's lock. The loom model asserts one draw a page, and `slot-install-relaxed` still reds the publication. The batched draw of an object run's holes is recorded without condition, owed by the object fault-around (#1259): whichever of #1259 and this lands second draws a run's holes in one hold. The free batch is a superframe's worth by name, and the promise doc is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cator's lock Main's populate already fills an object range's whole run (#1245), so a region written whole after it is made, such as a 32 MiB spawn image, drew a frame a page, one hold of the frame allocator's lock each: 8,192 where drawing the region whole took 16. The orchestrator decided to build the batch here rather than wait for #1259. `SharedMemObject::pages()` answers a run of pages at once, at most a fault's run, and `page()` goes through it. For a region, `slot::frames` takes every frame installed with one Acquire load each; if any page it was asked for is a hole it takes the region's first-touch lock, looks again, and has the holes still left drawn by one call, `pmm::alloc_promised_zeroed_into`: one hold of the allocator's lock for the draw, zeroing outside it, and one Release store a page to install. The object arm of `populate` asks for its run this way, and `create_whole` draws an inbox's rings a run at a time. The loom model gains two runs sharing a page: each page draws once, the shared page's one frame is handed to both, and each run draws in one call at most; `slot-install-relaxed` still reds the hand-over models. The first-touch record says it is built and carries what it owes on metal; the window record's two bullets go, the batch being built and the reading moved to the record of the change it measures. The slot's comments are trimmed as review r2 suggested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lt-around does The rebase onto #1259 at main 06fcb81 kept step 2's per-arm `around` in `populate` so every replayed commit compiled; this restores #1245's shape on main, one `around` above the match for every kind of range, with the paged arm's index lookup beneath it. No behaviour changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1259's rebase onto main 06fcb81 (head 1f8a0e4; LAND before at 3970ec0)ArchitectureThe rebase does not change the design. #1259 still has one What I checked
FindingsNo blockers. SUGGESTION
EvidenceMy checks at 1f8a0e4, in a detached worktree with its own target directory, on a macOS arm64 host:
The author's numbers (462 passed, lint 5 of 5) agree with mine. Guest: run 43110347183 at 1f8a0e4 had started but had not finished when I posted. Metal: none, and none is a landing condition while the benches are down. LAND |
…t step 1 keeps until step 2 - `loader::Map::rights` says what it answers: the rights the handle naming the range's object must carry. - `SpawnMap`'s doc names `PermissionDenied` again: a handle without `MAP`, or without `WRITE` beside it for a writable share. - The memory track record names the exit for the lost `mprotect` widening of a shared segment: step 2's convert-on-protect. - The record says what step 1 relies on until step 2's revocation exists: an object a space maps keeps its frames for its life, so ROOT's pages stay non-reclaimable and ROOT is not served through a pager until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Head 8a55a5f: 83b9f64's message reworded as 866fd8b, so it no longer claims object ranges' fault-around, which is main's (#1245); the other five commits are replayed unchanged. The tree is identical to 1f8a0e4 ( |
…able one is shared and executable: /system text is ROOT's own pages Memory track stage 5 for `/system`, and step 1 of 7b (mapped paged objects). A running `/system` program's read-only segments were copied out of ROOT's boot pages a page at each first touch, so every process of one binary held its own copy of the text and paid a fault and a 4 KiB copy per page. They are now ROOT's pages, mapped. - ABI: `vm_map(object, offset, len, prot, at)` (SYS 106) replaces `shm_map`, which mapped a whole object anywhere. A range of whole pages of the object goes anywhere or at a fixed place, as `vm_reserve` places. - Kernel: one pure rule, `kernel::vm::Shared::maps`, decides what a space may map: executable only where nobody can write the object (a boot image or part of one), writable only where somebody can, a device's window whole. `SharedMemObject::mappable` applies it for `vm_map` and a spawn. (Object ranges' 64 KiB fault-around is main's, from #1245.) - Spawn: `SpawnMap::backing` says copy or share. `toyos::elf::program:: lay_out` shares a segment the program cannot write, whose bytes are its file's pages with nothing past them, from an image nobody can write (`Source::Immutable`), and copies everything else. kimage FORMAT 2 carries the word for the supervisor. - `toyos::dl` maps such a library segment from its immutable image and copies only the rest; a segment inside the window relocations write is never mapped. - A shared segment's mapping is never widened, so a program can no longer `mprotect` its own read-only segment writable; recorded in the memory track record. - Guest: `vm_map_shares` (x86-64, and `virt_vm_map_shares` on AArch64, where no other row maps a shared page executable): a range lands at its fixed place, a writable region is never executed, an image is executed and never written, and a program's read-only segments take no private frame and fault a store. - The memory track records the orchestrator's stage-6 decision: 7b's step 3 maps images from every pager-backed volume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`Image::handle` became `Image::source` and `lay_out` takes a `Source`, but three guest test sites still passed a bare handle: the shared image helper, `startup_block`, and `spawn_image_object`, whose object is a writable copy of its own and so lays out as `Source::Copied`, every range copied, which is what that test is about. The guest test binaries did not build. No host check builds them; staging the metal images did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t step 1 keeps until step 2 - `loader::Map::rights` says what it answers: the rights the handle naming the range's object must carry. - `SpawnMap`'s doc names `PermissionDenied` again: a handle without `MAP`, or without `WRITE` beside it for a writable share. - The memory track record names the exit for the lost `mprotect` widening of a shared segment: step 2's convert-on-protect. - The record says what step 1 relies on until step 2's revocation exists: an object a space maps keeps its frames for its life, so ROOT's pages stay non-reclaimable and ROOT is not served through a pager until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test read its own peak memory through `this_process().stats()`, which the kernel refuses: a process's handle to itself carries `WRITE`, `DUP` and `TRANSFER`, and stats need `READ`. The AArch64 job case ran it first and ended at that call (`PermissionDenied`, guest run 43062438931); the x86-64 shared boots run the same binary and would have too. Three runs of the program now read nothing, the read-only table and the writable table, and the spawner reads each one's peak off its end through the handle its spawn answers. The claims are unchanged: the read-only table costs less than an eighth of its bytes over the run that reads nothing, the writable one at least three quarters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both benches are down, and metal is no longer a landing condition (the orchestrator's ruling). The memory track records what step 1 owes there, for one batched boot of main when a bench returns: the whole testcases boot with `vm_map_shares`'s x86-64 verdict, shared-debug and proctreecase on both benches, every job exiting 0; and osbench's spawn rows, main before against main after, no slower. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e seen `peak_of` built a `Spawn` by hand with no descriptors, so a reader's assertion messages went nowhere and a failure read only as an exit of 101. A reader is now spawned with `Command`, as the storer already is, inheriting stdio, and its peak is read off its end through its `Child`'s handle, which carries the spawner's `READ` (as `job_fairness` reads a child's accounting). The measurement and its claims are unchanged. The comment on what is measured names the reader. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Rebased onto main 3b93f37; head 04b1235. The one conflict was |
|
Orchestrator: rebased onto main 3b93f37 at 04b1235. |
… lock Two touches of a hole both drew before racing to install, so the loser drew against a promise its page did not hold, and the machine's promised count fell short of the promises outstanding until it gave the frame back. Each region now has a first-touch lock (`sync::Lock`): a touch takes an installed frame with one Acquire load, and a hole is drawn by the touch that, holding the lock, still finds it a hole, and installed with a Release store. Every draw is the page's own promise's; `discard` keeps #1250's caller and Safety text. The lock order is space, region, allocator, and nothing else takes the region's lock. The loom model asserts one draw a page, and `slot-install-relaxed` still reds the publication. The batched draw of an object run's holes is recorded without condition, owed by the object fault-around (#1259): whichever of #1259 and this lands second draws a run's holes in one hold. The free batch is a superframe's worth by name, and the promise doc is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cator's lock Main's populate already fills an object range's whole run (#1245), so a region written whole after it is made, such as a 32 MiB spawn image, drew a frame a page, one hold of the frame allocator's lock each: 8,192 where drawing the region whole took 16. The orchestrator decided to build the batch here rather than wait for #1259. `SharedMemObject::pages()` answers a run of pages at once, at most a fault's run, and `page()` goes through it. For a region, `slot::frames` takes every frame installed with one Acquire load each; if any page it was asked for is a hole it takes the region's first-touch lock, looks again, and has the holes still left drawn by one call, `pmm::alloc_promised_zeroed_into`: one hold of the allocator's lock for the draw, zeroing outside it, and one Release store a page to install. The object arm of `populate` asks for its run this way, and `create_whole` draws an inbox's rings a run at a time. The loom model gains two runs sharing a page: each page draws once, the shared page's one frame is handed to both, and each run draws in one call at most; `slot-install-relaxed` still reds the hand-over models. The first-touch record says it is built and carries what it owes on metal; the window record's two bullets go, the batch being built and the reading moved to the record of the change it measures. The slot's comments are trimmed as review r2 suggested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lock Two touches of a hole both drew before racing to install, so the loser drew against a promise its page did not hold, and the machine's promised count fell short of the promises outstanding until it gave the frame back. Each region now has a first-touch lock (`sync::Lock`): a touch takes an installed frame with one Acquire load, and a hole is drawn by the touch that, holding the lock, still finds it a hole, and installed with a Release store. Every draw is the page's own promise's; `discard` keeps #1250's caller and Safety text. The lock order is space, region, allocator, and nothing else takes the region's lock. The loom model asserts one draw a page, and `slot-install-relaxed` still reds the publication. The batched draw of an object run's holes is recorded without condition, owed by the object fault-around (#1259): whichever of #1259 and this lands second draws a run's holes in one hold. The free batch is a superframe's worth by name, and the promise doc is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cator's lock Main's populate already fills an object range's whole run (#1245), so a region written whole after it is made, such as a 32 MiB spawn image, drew a frame a page, one hold of the frame allocator's lock each: 8,192 where drawing the region whole took 16. The orchestrator decided to build the batch here rather than wait for #1259. `SharedMemObject::pages()` answers a run of pages at once, at most a fault's run, and `page()` goes through it. For a region, `slot::frames` takes every frame installed with one Acquire load each; if any page it was asked for is a hole it takes the region's first-touch lock, looks again, and has the holes still left drawn by one call, `pmm::alloc_promised_zeroed_into`: one hold of the allocator's lock for the draw, zeroing outside it, and one Release store a page to install. The object arm of `populate` asks for its run this way, and `create_whole` draws an inbox's rings a run at a time. The loom model gains two runs sharing a page: each page draws once, the shared page's one frame is handed to both, and each run draws in one call at most; `slot-install-relaxed` still reds the hand-over models. The first-touch record says it is built and carries what it owes on metal; the window record's two bullets go, the batch being built and the reading moved to the record of the change it measures. The slot's comments are trimmed as review r2 suggested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lock Two touches of a hole both drew before racing to install, so the loser drew against a promise its page did not hold, and the machine's promised count fell short of the promises outstanding until it gave the frame back. Each region now has a first-touch lock (`sync::Lock`): a touch takes an installed frame with one Acquire load, and a hole is drawn by the touch that, holding the lock, still finds it a hole, and installed with a Release store. Every draw is the page's own promise's; `discard` keeps #1250's caller and Safety text. The lock order is space, region, allocator, and nothing else takes the region's lock. The loom model asserts one draw a page, and `slot-install-relaxed` still reds the publication. The batched draw of an object run's holes is recorded without condition, owed by the object fault-around (#1259): whichever of #1259 and this lands second draws a run's holes in one hold. The free batch is a superframe's worth by name, and the promise doc is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cator's lock Main's populate already fills an object range's whole run (#1245), so a region written whole after it is made, such as a 32 MiB spawn image, drew a frame a page, one hold of the frame allocator's lock each: 8,192 where drawing the region whole took 16. The orchestrator decided to build the batch here rather than wait for #1259. `SharedMemObject::pages()` answers a run of pages at once, at most a fault's run, and `page()` goes through it. For a region, `slot::frames` takes every frame installed with one Acquire load each; if any page it was asked for is a hole it takes the region's first-touch lock, looks again, and has the holes still left drawn by one call, `pmm::alloc_promised_zeroed_into`: one hold of the allocator's lock for the draw, zeroing outside it, and one Release store a page to install. The object arm of `populate` asks for its run this way, and `create_whole` draws an inbox's rings a run at a time. The loom model gains two runs sharing a page: each page draws once, the shared page's one frame is handed to both, and each run draws in one call at most; `slot-install-relaxed` still reds the hand-over models. The first-touch record says it is built and carries what it owes on metal; the window record's two bullets go, the batch being built and the reading moved to the record of the change it measures. The slot's comments are trimmed as review r2 suggested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The past-the-object case pushed a `SpawnMap` without `backing`, a field the ABI gained with #1259 after the case was written; it is a copy range (`SpawnBacking::Copy`), as every range this test builds by hand is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lock Two touches of a hole both drew before racing to install, so the loser drew against a promise its page did not hold, and the machine's promised count fell short of the promises outstanding until it gave the frame back. Each region now has a first-touch lock (`sync::Lock`): a touch takes an installed frame with one Acquire load, and a hole is drawn by the touch that, holding the lock, still finds it a hole, and installed with a Release store. Every draw is the page's own promise's; `discard` keeps #1250's caller and Safety text. The lock order is space, region, allocator, and nothing else takes the region's lock. The loom model asserts one draw a page, and `slot-install-relaxed` still reds the publication. The batched draw of an object run's holes is recorded without condition, owed by the object fault-around (#1259): whichever of #1259 and this lands second draws a run's holes in one hold. The free batch is a superframe's worth by name, and the promise doc is rewrapped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cator's lock Main's populate already fills an object range's whole run (#1245), so a region written whole after it is made, such as a 32 MiB spawn image, drew a frame a page, one hold of the frame allocator's lock each: 8,192 where drawing the region whole took 16. The orchestrator decided to build the batch here rather than wait for #1259. `SharedMemObject::pages()` answers a run of pages at once, at most a fault's run, and `page()` goes through it. For a region, `slot::frames` takes every frame installed with one Acquire load each; if any page it was asked for is a hole it takes the region's first-touch lock, looks again, and has the holes still left drawn by one call, `pmm::alloc_promised_zeroed_into`: one hold of the allocator's lock for the draw, zeroing outside it, and one Release store a page to install. The object arm of `populate` asks for its run this way, and `create_whole` draws an inbox's rings a run at a time. The loom model gains two runs sharing a page: each page draws once, the shared page's one frame is handed to both, and each run draws in one call at most; `slot-install-relaxed` still reds the hand-over models. The first-touch record says it is built and carries what it owes on metal; the window record's two bullets go, the batch being built and the reading moved to the record of the change it measures. The slot's comments are trimmed as review r2 suggested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
A running
/systemprogram's read-only segments were copied out of ROOT's boot pages, a page at each first touch, so every process of one binary held its own copy of its text and paid a fault and a 4 KiB copy per page. No memory object could be mapped executable, andshm_mapmapped only a whole object, anywhere. This is memory track stage 5 for/systemand step 1 of mapped paged objects (7b), whose design the orchestrator approved.Change
vm_map(object, offset, len, prot, at)(SYS 106, arguments inVmMapArgs) replacesshm_map: whole pages of a memory object, anywhere or at a fixed place, asvm_reserveplaces.SpawnMapgainsbacking(SpawnBacking::CopyorShare; 56 bytes), and kimageFORMAT2 carries it for the supervisor.kernel::vm::Shared::maps, decides what a space may map of a memory object: executable only where nobody can write it (a boot image or part of one), writable only where somebody can, a device's window whole.SharedMemObject::mappableapplies it tovm_mapand to a spawn's shared range; the handle's rights still bound the protection (MAP, andWRITEfor a writable mapping).toyos::elf::program::lay_outshares a segment the program cannot write whose bytes are its file's own pages with nothing past them, from an image nobody can write (Source::Immutable), and copies the rest.toyos::dlmaps such a library segment from its immutable image and copies only the rest; a segment inside the window relocations write is never mapped.kernel::vm::around): a lone touch maps its one page, and a touch beside a populated page maps its aligned 64 KiB run. This PR adds nothing to the fault path; its own hunk was dropped in the rebase.mprotectits own read-only segment writable. A page shared until its first write needs 7b's write faults (step 5); recorded in the memory track record.root-in-memory-costs-its-whole-sizenow says a/systemprogram's read-only pages are ROOT's own; its exit (the T14's boot line, and the used memory of 255 held children read again) stays owed.vm_map_shares(a range lands at its fixed place, a writable region is never executed, an image is executed and never written, a program's read-only segments take no private frame and fault a store): its x86-64 verdict is the shared metal boot's;virt_vm_map_sharesruns it on AArch64's job case, where no other row maps a shared page executable. It is declared inDRIVEN_AND_SHARED.Size at the head 3baf239, measured with
git diff --numstatagainst the merge base b566931: +820 / -254 over 39 files. Kernel +204 / -80 (of it the rule's host test, +39), ABI +86 / -30, SDK +265 / -86 (of it host tests, +134 / -45), guest tests and harness +198 / -19, build and kimage +10 / -6 (kimage's test +5 / -2), records +44 / -25, manual +13 / -8.Checks
Targeted under the owner's testing policy; the full
--ci hostbecause the change touches the build system (kimage) and the harness. The head 3baf239 is c991bfd plus review r1's comment, doc and record changes, which no check below can see. c991bfd is b29fb2b plus one commit that moves three guest test sites tolay_out'sSource(Image::source); no host check builds those binaries, which is why only the metal staging found them, so every host result at b29fb2b holds for the head.Host, on macOS arm64:
cargo test -p kernel --lib vm::,cargo test -p toyos --test elf_program,cargo test -p toyos --lib dl::,cargo test -p toyos-kimage: exit 0 each.an_object_maps_as_what_it_isholds the rule;a_segment_that_is_its_files_pages_is_mapped_and_reads_as_its_copyanda_segment_a_copy_alone_holds_is_never_mappedholddl's mapping. Run in the step 2 worktree, whosesdk/,lib/kimage/andkernel/pure/vm.rsare byte-identical to b29fb2b (git diff b29fb2b2d HEAD -- sdk lib/kimage kernel/pure/vm.rsis empty), while this branch's own tree was staging images.ReadExecmappable from a writable object turnsan_object_maps_as_what_it_isred; droppingdl's relocation-window check turnsa_segment_a_copy_alone_holds_is_never_mappedred. Exit 101 each, restored identical.cargo test --test toyos-checks -- checks::suite_splitat b29fb2b: exit 0 (it was red beforevm_map_sharesjoinedDRIVEN_AND_SHARED).cargo run -- --ci hostat b29fb2b: exit 1, 109 of 110 steps green, clippy and the kernel's models with their controls among them. The one red isllvm::tests::the_caller_s_environment_reaches_neither_the_build_nor_the_key(andkeyed_and_built, the child it re-runs), red on every macOS host and recorded inissues/the-llvm-environment-test-breaks-git-on-macos.md; this branch touches nothing it reads.Guest: run 43062438931 at c991bfd (
virt_vm_map_shares virt_user_mode bar_map_again bar_map_behind_a_bridge lent_device_memory_refused a_spawn_from_an_image_frees_it_when_the_child_ends libc_children a_drives_row_holds_one_process_per_function session_lifecycle) was green but forvirt_vm_map_shares. That red was the test's own: it read its own statistics through a handle that carries noREAD(PermissionDenied); 470bf6f reads each of three child runs' peaks through the spawner's handle instead (comment #issuecomment-6804509956). Every one of those rows boots/systemwith shared text; beyond that,virt_vm_map_sharesis the claim on AArch64, the BAR and lent-memory rows exercisevm_map's whole-window and rights rules, the spawn and libc rows a spawn's shared and copied ranges, andsession_lifecyclethe shared memory of a desktop session. Run 43081676127 at 470bf6f (virt_vm_map_shares bar_map_again bar_map_behind_a_bridge lent_device_memory_refused) is green, 4 passed of 4 (https://github.com/ToyOSOrg/evidence/tree/26147d225c0dca7e711a265b842bdfa6ecaa1f0d/r2); it stands for every row butvirt_vm_map_shares, whose binary review r2 changed. 3970ec0 (the head) spawns each reader withCommand, so its own assertion messages reach the log; run 43093948691 at 3970ec0 (virt_vm_map_shares) is this PR's landing run for it. Two earlier dispatches gave no result: 43049845595 named shared binaries as QEMU rows and was cancelled before it ran; 43051591773 was cancelled once the guest binaries were found not to build.Owed on metal (both benches are down; recorded in the memory track record, and run on main in one batched boot when a bench returns):
testcasesboot, whosevm_map_sharesmember is the x86-64 verdict that a program's read-only segments take no private frame (spawn_costamong its other members), and theshared-debugandproctreecaseboots. Main must show every job exit 0.process.spawn_wait,proc.spawn_wait.*andproc.posix_spawn_wait.*, main before this PR against main after, four boots a side. Main after must be no slower. Whether sharing the text makes a spawn faster is not measured on metal.Risks and owed
virt_vm_map_sharesunder emulation.syscall/ipc.rsconflicts, keep this branch's deletion ofsys_shm_map;syscall/proc.rs84-90 conflicts, keepMap::rights(&raw)and look the handle up asprocess::with_handles(|handles| handles.get::<SharedMemObject>(RawHandle(raw_handle), rights)).syscall/vm.rs:93(sys_vm_map) merges cleanly but stops compiling, because A thread carries its process's handles, and a lookup takes no lock but the table's #1246 turnsProcessData::handlesintoArc<Handles>, which has noget: switch it toprocess::with_handles(|handles| handles.get::<SharedMemObject>(h, map_rights(prot))).populate; its hunk, which hoistsaroundabove the match, is the one to keep.toyos::dlRELRO) and endow (spawn.rs,bar_map_again.rs,virtjobcase.toml,tests/toyos.rs) touch files this does.Evidence: https://github.com/ToyOSOrg/evidence/tree/8beddb49b730284eceb9564223da3faa551b729a (README.md maps each check to its command, revision, exit and log).
🤖 Generated with Claude Code