From 0045fd87ba61127f3476fe3edb048580a0a518ad Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:26:15 +0100 Subject: [PATCH] =?UTF-8?q?naming:=20shm=20=E2=86=92=20shared=5Fmemory=20?= =?UTF-8?q?=E2=80=94=20'shm'=20is=20a=20Unix=20clipping,=20not=20an=20acro?= =?UTF-8?q?nym?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per docs/coding-standards.md (no Unix-abbreviation exception): syscalls shared_memory_create/map/physical, kernel SharedMemoryObject + handlers, runtime.shared_memory (library/runtime/shared-memory.zig), the shared-memory-server/-client test services, the shared-memory QEMU case, and docs incl. vdso.md's danos_shared_memory_*. 87/87 QEMU tests pass. --- build.zig | 12 +-- docs/display-plan.md | 4 +- docs/display-v2-plan.md | 32 ++++---- docs/display-v2.md | 10 +-- docs/display.md | 6 +- docs/threading-plan.md | 4 +- docs/threading.md | 4 +- docs/vdso.md | 4 +- library/runtime/runtime.zig | 4 +- .../runtime/{shm.zig => shared-memory.zig} | 10 +-- system/abi.zig | 8 +- system/drivers/virtio-gpu/virtio-gpu.zig | 12 +-- system/kernel/architecture/x86_64/cpu.zig | 4 +- system/kernel/ipc-synchronous.zig | 42 +++++----- system/kernel/process.zig | 76 +++++++++---------- system/kernel/scheduler.zig | 2 +- system/kernel/tests.zig | 22 +++--- system/services/display/backend.zig | 2 +- system/services/display/display.zig | 4 +- .../shared-memory-client.zig} | 24 +++--- .../shared-memory-server.zig | 44 +++++++++++ system/services/shm-server/shm-server.zig | 44 ----------- test/qemu_test.py | 10 +-- 23 files changed, 192 insertions(+), 192 deletions(-) rename library/runtime/{shm.zig => shared-memory.zig} (84%) rename system/services/{shm-client/shm-client.zig => shared-memory-client/shared-memory-client.zig} (50%) create mode 100644 system/services/shared-memory-server/shared-memory-server.zig delete mode 100644 system/services/shm-server/shm-server.zig diff --git a/build.zig b/build.zig index b9fa826..dea316f 100644 --- a/build.zig +++ b/build.zig @@ -540,8 +540,8 @@ pub fn build(b: *std.Build) void { const display_exe = addThreadedUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display", "system/services/display/display.zig"); const display_demo_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display-demo", "system/services/display-demo/display-demo.zig"); const virtio_gpu_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "virtio-gpu", "system/drivers/virtio-gpu/virtio-gpu.zig"); - const shm_server_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shm-server", "system/services/shm-server/shm-server.zig"); - const shm_client_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shm-client", "system/services/shm-client/shm-client.zig"); + const shared_memory_server_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-server", "system/services/shared-memory-server/shared-memory-server.zig"); + const shared_memory_client_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-client", "system/services/shared-memory-client/shared-memory-client.zig"); const fat_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "fat-test", "system/services/fat/fat-test.zig"); const pci_bus_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "pci-bus", "system/drivers/pci-bus/pci-bus.zig"); // The PCI bus driver decodes each function's class triple to human names in its @@ -618,10 +618,10 @@ pub fn build(b: *std.Build) void { mk_run.addFileArg(display_demo_exe.getEmittedBin()); mk_run.addArg("virtio-gpu"); mk_run.addFileArg(virtio_gpu_exe.getEmittedBin()); - mk_run.addArg("shm-server"); - mk_run.addFileArg(shm_server_exe.getEmittedBin()); - mk_run.addArg("shm-client"); - mk_run.addFileArg(shm_client_exe.getEmittedBin()); + mk_run.addArg("shared-memory-server"); + mk_run.addFileArg(shared_memory_server_exe.getEmittedBin()); + mk_run.addArg("shared-memory-client"); + mk_run.addFileArg(shared_memory_client_exe.getEmittedBin()); mk_run.addArg("pci-bus"); mk_run.addFileArg(pci_bus_exe.getEmittedBin()); mk_run.addArg("crash-test"); diff --git a/docs/display-plan.md b/docs/display-plan.md index b19a1cc..8729e0b 100644 --- a/docs/display-plan.md +++ b/docs/display-plan.md @@ -134,7 +134,7 @@ Prove the pipeline end-to-end from a separate process. every IPC message at `MESSAGE_MAXIMUM` = 256 — so `replyWait` rejected the oversized receive buffer with `-E2BIG` and the serve loop had been *spinning* since D2 (unseen, as D2/D3 matched init-time heartbeats). Set it to 256; `blit_tile` is now explicitly - a small-tile path (≤ 54 px inline), larger bitmaps being the deferred shm surface. + a small-tile path (≤ 54 px inline), larger bitmaps being the deferred shared-memory surface. **Gate (met):** `python3 test/qemu_test.py display-demo` spawns the service + `display-demo`; the demo drives a run of frames of motion through the layer client API and logs @@ -172,7 +172,7 @@ documented (docs/display.md): no runtime mode-setting (native backend) and no tr ## Deferred (explicitly not in this plan) - **Shared-memory surfaces** — generalize M13 capability passing to memory objects - (`shm_create`/`shm_map`), so bitmap clients hand the compositor a rendered surface + (`shared_memory_create`/`shared_memory_map`), so bitmap clients hand the compositor a rendered surface instead of drawing commands. The compositor's layer model already anticipates it. - **Native backend (Bochs DISPI, then virtio-gpu)** — behind the same internal backend interface as the dumb framebuffer: EDID mode list + runtime resolution/bpp change + diff --git a/docs/display-v2-plan.md b/docs/display-v2-plan.md index 849c6e9..7da5123 100644 --- a/docs/display-v2-plan.md +++ b/docs/display-v2-plan.md @@ -10,7 +10,7 @@ lands on its own and ends in a **verifiable gate** — shaped for a `/loop` run, - **Dynamic hot-attach**: boot on GOP, upgrade to native when the driver **announces** (push, not polling); re-attach across driver restarts; GOP is the floor for "no driver ever," not a live fall-back after a reprogram. -- **v2 builds the `shm` capability** (endpoints → memory objects), shared with the future +- **v2 builds the shared-memory capability** (endpoints → memory objects), shared with the future client-surface path. - The compositor's layers/back-buffer/damage are **unchanged**; only scanout is pluggable. @@ -26,7 +26,7 @@ kebab-case file names, no `Co-Authored-By` trailers. New user binaries go throug **Every gate is serial-checkable — no screenshots** (this plan is built to run unattended). Where "does it actually display" would otherwise need a human eyeball, the code **reads its -own pixels back**: the scanout resource is CPU-visible RAM (shm-backed) and the back buffer +own pixels back**: the scanout resource is CPU-visible RAM (shared-memory-backed) and the back buffer is cacheable, so a driver/compositor can write a known value, read it back, and log a pass/fail — and a virtio `resource_flush` is confirmed by the device **acking it on the used ring**. Those two together (pixel-readback + flush-ack) are the automated stand-in for @@ -57,25 +57,25 @@ Extract scanout from the compositor so today's path becomes one backend among fu **Gate (met):** `display-service` + `display-demo` pass **unchanged** (pure refactor; GOP is the only backend), and `zig build test` stays green. -## V2 — The `shm` cross-process memory capability (kernel) ✅ +## V2 — The shared-memory cross-process capability (kernel) ✅ -- [x] [abi.zig](../system/abi.zig): `shm_create` (34) / `shm_map` (35) syscalls + a - `shm_test` service id. Handlers in process.zig: `shm_create(len)` allocates contiguous, +- [x] [abi.zig](../system/abi.zig): `shared_memory_create` (34) / `shared_memory_map` (35) syscalls + a + `shared_memory_test` service id. Handlers in process.zig: `shared_memory_create(len)` allocates contiguous, zeroed, **cacheable** frames, wraps them in a refcounted object, installs a capability - handle, maps them into the caller's shm arena → returns virtual_address + handle; `shm_map(cap)` + handle, maps them into the caller's shared-memory arena → returns virtual_address + handle; `shared_memory_map(cap)` maps the same physical pages into the receiver. Reclaimed on death (see below). - [x] The capability core (ipc-synchronous.zig) is now **kind-tagged**: `scheduler.Task`'s handle table holds `HandleObject{kind, ptr}`; `closeHandles` and `shareCapability` - dispatch by kind, so an `ShmObject` rides an `ipc_call` `send_cap` exactly like an + dispatch by kind, so a `SharedMemoryObject` rides an `ipc_call` `send_cap` exactly like an endpoint and frees only when its last capability drops. `mapUserSharedInto` (paging) maps WB-cacheable + `device_grant`, so a sharer's teardown never frees the shared frames — the object owns them. -- [x] `library/runtime/shm.zig` (+ barrel export): `create(len) -> Region{ptr, handle, len}`, +- [x] `library/runtime/shared-memory.zig` (+ barrel export): `create(len) -> Region{ptr, handle, len}`, `map(handle) -> ptr`. -**Gate (met):** `python3 test/qemu_test.py shm` — `shm-client` creates a region, writes a -pattern, and passes its capability to `shm-server` as an `ipc_call` send_cap; the server -`shm_map`s it and reads the **same bytes** back → `shm: shared 4096 bytes ok`. Guardrail: +**Gate (met):** `python3 test/qemu_test.py shared-memory` — `shared-memory-client` creates a region, writes a +pattern, and passes its capability to `shared-memory-server` as an `ipc_call` send_cap; the server +`shared_memory_map`s it and reads the **same bytes** back → `shared-memory: shared 4096 bytes ok`. Guardrail: `ipc`/`ipc-call`/`ipc-cap`, `supervision`, `dma`, `usermem`, `display-service`, and host tests all still pass — the handle-table change broke no existing IPC. @@ -88,7 +88,7 @@ tests all still pass — the handle-table change broke no existing IPC. VERSION_1, and stand up the control virtqueue in coherent DMA. `virtio-gpu-protocol.zig` + `virtio-pci.zig` for the control/transport structs (host-tested sizes). - [x] Create a 2D scanout resource backed by a coherent DMA region (V4 swaps this for the - shm-shared surface), `attach_backing`, `set_scanout` to scanout 0, `transfer_to_host_2d` + shared-memory surface), `attach_backing`, `set_scanout` to scanout 0, `transfer_to_host_2d` + `resource_flush` of a test pattern, and wait on the used ring. - [x] Register a `scanout` service (`ServiceId.scanout` = 11). @@ -102,7 +102,7 @@ end without a screenshot (the used-ring ack is the device confirming it consumed ## V4 — The native backend + hot-attach ✅ -- [x] `backend.VirtioGpu` in the compositor: `surface()` = the shared `shm` scanout surface +- [x] `backend.VirtioGpu` in the compositor: `surface()` = the shared-memory scanout surface (the compositor composes straight into the device's resource backing; x86 DMA is coherent, so the cacheable shared pages need no flush), `present(damage)` = a `present` request over the driver's `.scanout` endpoint (→ transfer-to-host + resource flush). @@ -112,7 +112,7 @@ end without a screenshot (the used-ring ack is the device confirming it consumed driver registered it), switches backend, and re-composites the current frame full-screen. The present is deferred to a one-shot timer so it runs *after* the reply unblocks the driver and it serves `.scanout` — presenting inline would deadlock. -- [x] Boot still starts on `backend.Gop`; the upgrade happens on announce. `shm_physical` (a +- [x] Boot still starts on `backend.Gop`; the upgrade happens on announce. `shared_memory_physical` (a new syscall) gives the driver the guest-physical of the shared surface for `attach_backing`. **Gate (met):** the `display-native` case (QEMU `-device virtio-gpu-pci`, `mem` bumped since it @@ -159,14 +159,14 @@ kills the virtio-gpu driver once after it hellos; the restart policy respawns it re-announces, and the compositor logs `display: scanout re-attached` after the initial `display: scanout upgraded to virtio-gpu`, with no CPU exception / panic (the compositor survives) — passing 3/3. All v1 + v2 cases (host tests, `ipc`/`ipc-call`/`ipc-cap`, -`supervision`, `shm`, `display-service`, `display-demo`, `virtio-gpu`, `display-native`, +`supervision`, `shared-memory`, `display-service`, `display-demo`, `virtio-gpu`, `display-native`, `display-modeset`) pass; default `zig build` is clean. --- ## Deferred (explicitly not in this plan) -- **Client-rendered surfaces** — now unblocked by the `shm` capability (V2): an app renders +- **Client-rendered surfaces** — now unblocked by the shared-memory capability (V2): an app renders its own bitmap and hands the compositor a reference. A natural follow-on. - **Bochs DISPI backend** — a simpler second native backend (mode-set only, dumb scanout); slots behind the same interface if wanted. diff --git a/docs/display-v2.md b/docs/display-v2.md index 15a692f..8061a55 100644 --- a/docs/display-v2.md +++ b/docs/display-v2.md @@ -1,7 +1,7 @@ # The display service v2: a pluggable scanout backend **Status: complete (V1–V6).** The compositor boots on the GOP framebuffer and, when a -virtio-gpu driver announces itself, hot-attaches a native backend over the shared `shm` +virtio-gpu driver announces itself, hot-attaches a native backend over the shared-memory scanout surface — with runtime mode-setting, EDID, and fenced presents, and it re-attaches across driver restarts. All serial-gated (see [display-v2-plan.md](display-v2-plan.md)). @@ -77,9 +77,9 @@ deferred (docs/display.md, "What v1 does not do"). v2 builds it: the natural gen of M13 capability-passing from *endpoints* to *memory objects* — ``` -shm_create(len) -> {handle, virtual_address} // a shareable, page-aligned RAM region +shared_memory_create(len) -> {handle, virtual_address} // a shareable, page-aligned RAM region … pass `handle` as the send_cap on an ipc_call … -shm_map(cap) -> virtual_address // the receiver maps the same physical pages +shared_memory_map(cap) -> virtual_address // the receiver maps the same physical pages ``` The payoff is leverage: the **same** primitive unlocks **both** native GPU drivers *and* @@ -92,7 +92,7 @@ A new ring-3 driver process (the topology v1 anticipated — "split the driver f compositor when a second backend arrives"). It claims the virtio-gpu PCI function, and: - sets up the **virtqueues** (control + cursor) and the device's config space, -- creates a **2D scanout resource** backed by an `shm` region, `attach_backing`s it, +- creates a **2D scanout resource** backed by a shared-memory region, `attach_backing`s it, `set_scanout`s it to a CRTC, and `resource_flush`es damaged rectangles, - reads **EDID** (the `GET_EDID` control command) for the mode list, and `set_scanout` at a chosen mode for **runtime mode-setting**, @@ -128,7 +128,7 @@ path in VMs**, where danos development happens. The framebuffer floor never goes re-attach across driver restarts; GOP is the floor for "no driver ever," not a live fall-back after a reprogram. - **Detection = push** (the driver announces to `.display`), not compositor polling. -- **v2 builds the `shm` capability** (endpoints → memory objects), shared with the future +- **v2 builds the shared-memory capability** (endpoints → memory objects), shared with the future client-surface path. ## See also diff --git a/docs/display.md b/docs/display.md index 826f0b7..e6b04dc 100644 --- a/docs/display.md +++ b/docs/display.md @@ -95,7 +95,7 @@ rest of the system hasn't had to face: ┌────────────────────────────────────┬──────────────────────────────────────┐ drawing clients (v1) surface clients (deferred) runtime.display commands: runtime.display surfaces: - create_layer / configure_layer shm_create → pass as a capability → + create_layer / configure_layer shared_memory_create → pass as a capability → fill_rect / blit_tile / damage the compositor maps & composites the present client-rendered bitmap directly ``` @@ -260,8 +260,8 @@ both are clean additions behind the interfaces v1 establishes. to render into its *own* buffer and hand the compositor a *reference*, not a stream of commands. That needs the missing cross-process shared-memory primitive — best built as the natural generalization of the existing M13 [capability passing](driver-model.md) - from *endpoints* to *memory objects* (`shm_create(len) → {cap, virtual_address}`, pass `cap` on - an `ipc_call`, receiver `shm_map(cap) → virtual_address`). v1 avoids it because server-owned + from *endpoints* to *memory objects* (`shared_memory_create(len) → {cap, virtual_address}`, pass `cap` on + an `ipc_call`, receiver `shared_memory_map(cap) → virtual_address`). v1 avoids it because server-owned surfaces already prove the whole pipeline. - **Runtime mode-setting (a native backend).** Detecting the EDID mode list and changing diff --git a/docs/threading-plan.md b/docs/threading-plan.md index 3f245c8..bc53c42 100644 --- a/docs/threading-plan.md +++ b/docs/threading-plan.md @@ -14,7 +14,7 @@ lands on its own and ends in a **verifiable gate** — shaped for a `/loop` run, - **Blocking is futex-backed, never spin-backed** — waiters park in the kernel so an idle core still halts ([halting.md](halting.md)). - **New syscalls are private**: extend [abi.zig](../system/abi.zig) `SystemCall` after - `shm_physical = 36` (`thread_spawn = 37`, `thread_exit = 38`, `current_core = 39`, + `shared_memory_physical = 36` (`thread_spawn = 37`, `thread_exit = 38`, `current_core = 39`, `futex_wait = 40`, `futex_wake = 41`) + a `library/runtime` wrapper; user code never names a number. - **Restart granularity stays the process** — a faulting thread kills its process; the supervisor restarts the process, which respawns its threads. @@ -457,7 +457,7 @@ clean. ## Deferred (explicitly not in this plan) - **Cross-process shared-memory futex** — the `(address_space, virtual_address)` key can become a - physical-address key so two processes share a futex through an [shm](display-v2.md) + physical-address key so two processes share a futex through a [shared-memory](display-v2.md) region. Not needed for intra-process threads. - **Per-thread priorities / affinity distinct from the process** — threads inherit the process priority ([scheduling.md](scheduling.md)); revisit only if it earns its keep. diff --git a/docs/threading.md b/docs/threading.md index e87080d..29cd0e5 100644 --- a/docs/threading.md +++ b/docs/threading.md @@ -133,7 +133,7 @@ Deviations from `std.Thread`, called out honestly: ## Kernel primitives (new private syscalls) Four new entries extend [abi.zig](../system/abi.zig) `SystemCall` after -`shm_physical = 36`, each with a `library/runtime` wrapper: +`shared_memory_physical = 36`, each with a `library/runtime` wrapper: | Syscall | Signature | Purpose | |---|---|---| @@ -210,7 +210,7 @@ Keying: threads share an address space, so a **virtual address within that addre identifies a futex uniquely; the kernel keys its wait queue by `(address_space_root, virtual_address)`. Keying by the **physical** address instead (translate `virtual_address -> physical_address` on entry) is a deliberate forward door: it lets two *processes* share a futex through an -[shm](display-v2.md) region later, without changing the API. We start with the +[shared-memory](display-v2.md) region later, without changing the API. We start with the private-per-address-space key and note the physical-key upgrade. No spinning: a contended lock parks the task in the kernel and the core is free to run diff --git a/docs/vdso.md b/docs/vdso.md index 3c2d89c..24b9946 100644 --- a/docs/vdso.md +++ b/docs/vdso.md @@ -119,7 +119,7 @@ One table entry per kernel call, C ABI (System V AMD64), names prefixed returns are `u64`, errors return as negative values exactly as today. The calls that return two values in `rax:rdx` today — `dma_alloc` -(virtual_address + physical_address), `msi_bind` (address + data), `shm_create` (virtual_address + handle) — +(virtual_address + physical_address), `msi_bind` (address + data), `shared_memory_create` (virtual_address + handle) — become functions returning a two-`u64` struct. The System V ABI returns a 16-byte struct in `rax:rdx`, so the stub is a plain `syscall; ret` — the C-ABI spelling of the existing convention, at zero cost. @@ -129,7 +129,7 @@ Grouped as `abi.zig` groups them: | Group | Functions | |-------|-----------| | process | `danos_exit`, `danos_yield`, `danos_sleep`, `danos_spawn`, `danos_process_enumerate`, `danos_process_kill`, `danos_process_exit_reason`, `danos_process_subscribe`, `danos_process_signal`, `danos_signal_bind` | -| memory | `danos_mmap`, `danos_munmap`, `danos_dma_alloc`, `danos_dma_free`, `danos_shm_create`, `danos_shm_map`, `danos_shm_physical` | +| memory | `danos_mmap`, `danos_munmap`, `danos_dma_alloc`, `danos_dma_free`, `danos_shared_memory_create`, `danos_shared_memory_map`, `danos_shared_memory_physical` | | ipc | `danos_endpoint_create`, `danos_ipc_register`, `danos_ipc_lookup`, `danos_ipc_call`, `danos_ipc_reply_wait`, `danos_ipc_send` | | devices | `danos_device_enumerate`, `danos_device_claim`, `danos_device_register`, `danos_mmio_map`, `danos_irq_bind`, `danos_irq_ack`, `danos_msi_bind`, `danos_io_read`, `danos_io_write` | | time | `danos_clock`, `danos_wall_clock`, `danos_timer_bind` | diff --git a/library/runtime/runtime.zig b/library/runtime/runtime.zig index 0533d7c..b716faf 100644 --- a/library/runtime/runtime.zig +++ b/library/runtime/runtime.zig @@ -38,9 +38,9 @@ pub const device = @import("device.zig"); pub const dma = @import("dma.zig"); /// Shared cacheable memory: create a region + capability, pass the capability to another -/// process (an `ipc_call` send_cap), map the same pages there. See library/runtime/shm.zig +/// process (an `ipc_call` send_cap), map the same pages there. See library/runtime/shared-memory.zig /// and docs/display-v2.md. -pub const shm = @import("shm.zig"); +pub const shared_memory = @import("shared-memory.zig"); /// USB class-driver client: open a device on the xHCI bus and drive it /// (control / interrupt / bulk transfers). See library/runtime/usb.zig. diff --git a/library/runtime/shm.zig b/library/runtime/shared-memory.zig similarity index 84% rename from library/runtime/shm.zig rename to library/runtime/shared-memory.zig index a75056c..9251e01 100644 --- a/library/runtime/shm.zig +++ b/library/runtime/shared-memory.zig @@ -1,7 +1,7 @@ -//! User-space shared memory: `shm_create` / `shm_map`. A process creates a shareable, +//! User-space shared memory: `shared_memory_create` / `shared_memory_map`. A process creates a shareable, //! zeroed, cacheable RAM region and gets back a pointer plus a **capability handle**; it //! passes that handle to another process as an `ipc_call` send_cap, and the receiver -//! `shm_map`s it to map the same physical pages. The kernel primitive under the display +//! `shared_memory_map`s it to map the same physical pages. The kernel primitive under the display //! compositor↔native-driver and app↔compositor surface paths (docs/display-v2.md). The //! generalization of capability passing from endpoints to memory objects. @@ -30,7 +30,7 @@ pub fn create(len: usize) ?Region { asm volatile ("syscall" : [rax] "={rax}" (rax), [rdx] "={rdx}" (rdx), - : [n] "{rax}" (@intFromEnum(abi.SystemCall.shm_create)), + : [n] "{rax}" (@intFromEnum(abi.SystemCall.shared_memory_create)), [a0] "{rdi}" (len), : .{ .rcx = true, .r11 = true, .memory = true }); if (failed(rax)) return null; @@ -41,7 +41,7 @@ pub fn create(len: usize) ?Region { /// `ipc_call` send_cap) into its address space — the same physical pages the creator sees. /// Returns the pointer, or null on failure. pub fn map(handle: ipc.Handle) ?[*]u8 { - const r = sc.systemCall1(.shm_map, handle); + const r = sc.systemCall1(.shared_memory_map, handle); if (failed(r)) return null; return @ptrFromInt(r); } @@ -51,7 +51,7 @@ pub fn map(handle: ipc.Handle) ?[*]u8 { /// the region length is all a device needs — e.g. a virtio-gpu driver programming an /// `attach_backing`. Returns null on failure. pub fn physical(handle: ipc.Handle) ?usize { - const r = sc.systemCall1(.shm_physical, handle); + const r = sc.systemCall1(.shared_memory_physical, handle); if (failed(r)) return null; return r; } diff --git a/system/abi.zig b/system/abi.zig index 7ab350b..52b5d99 100644 --- a/system/abi.zig +++ b/system/abi.zig @@ -60,9 +60,9 @@ pub const SystemCall = enum(u64) { timer_bind = 31, // timer_bind(endpoint, ms) -> 0/-errno: one-shot timer — posts a notification when ms elapse klog_read = 32, // klog_read(offset, ptr, len) -> bytes copied: copy the kernel RAM log buffer out to a user buffer (for persisting the boot log to disk) wall_clock = 33, // wall_clock() -> Unix epoch seconds (UTC): the RTC wall-clock time, for filesystem timestamps (mtime). Monotonic time is `clock`. - shm_create = 34, // shm_create(len) -> virtual_address (rax), handle (rdx): a shareable, zeroed, cacheable RAM region mapped into this AS; the handle is a capability passed to another process as an ipc_call send_cap (docs/display-v2.md) - shm_map = 35, // shm_map(cap) -> virtual_address: map the shared region named by a received capability into this address space (the same physical pages the creator sees) - shm_physical = 36, // shm_physical(cap) -> physical_address: the guest-physical base of a shared region held by capability, so a driver can program it into a device (e.g. virtio-gpu attach_backing); the pages are contiguous (docs/display-v2.md) + shared_memory_create = 34, // shared_memory_create(len) -> virtual_address (rax), handle (rdx): a shareable, zeroed, cacheable RAM region mapped into this AS; the handle is a capability passed to another process as an ipc_call send_cap (docs/display-v2.md) + shared_memory_map = 35, // shared_memory_map(cap) -> virtual_address: map the shared region named by a received capability into this address space (the same physical pages the creator sees) + shared_memory_physical = 36, // shared_memory_physical(cap) -> physical_address: the guest-physical base of a shared region held by capability, so a driver can program it into a device (e.g. virtio-gpu attach_backing); the pages are contiguous (docs/display-v2.md) thread_spawn = 37, // thread_spawn(entry, stack_top, arg, exit_endpoint) -> tid: start a task sharing the caller's address space at `entry` on `stack_top`, `arg` in rdi; exit_endpoint (a handle, or no_cap) is notified when it ends — how join waits (docs/threading.md) thread_exit = 38, // thread_exit(): end the calling thread, dropping one reference to its address space (destroyed on the last) current_core = 39, // current_core() -> index: the dense 0-based index of the core the caller is running on (for parallelism/affinity introspection) @@ -200,7 +200,7 @@ pub const ServiceId = enum(u32) { block = 7, // a block-device driver (USB mass storage today): read/write of fixed-size blocks, the storage a filesystem sits on fat = 8, // the FAT filesystem server; the VFS mounts it and forwards paths under its mount point (/mnt/usb) to it display = 9, // the display service: owns the framebuffer, composites a layer stack, presents frames (docs/display.md) - shm_test = 10, // the shm test server (V2): a client passes it a shared-memory capability, it maps + verifies (docs/display-v2.md) + shared_memory_test = 10, // the shared-memory test server (V2): a client passes it a shared-memory capability, it maps + verifies (docs/display-v2.md) scanout = 11, // a native scanout driver (virtio-gpu): the compositor finds it here to upgrade off the GOP framebuffer (docs/display-v2.md) _, }; diff --git a/system/drivers/virtio-gpu/virtio-gpu.zig b/system/drivers/virtio-gpu/virtio-gpu.zig index 273702e..c8961da 100644 --- a/system/drivers/virtio-gpu/virtio-gpu.zig +++ b/system/drivers/virtio-gpu/virtio-gpu.zig @@ -18,7 +18,7 @@ const runtime = @import("runtime"); const mmio = @import("mmio"); const device = runtime.device; const dma = runtime.dma; -const shm = runtime.shm; +const shared_memory = runtime.shared_memory; const system = runtime.system; const ipc = runtime.ipc; const dp = runtime.display_protocol; @@ -93,11 +93,11 @@ var bar_virtual: [6]usize = .{ 0, 0, 0, 0, 0, 0 }; var ring: dma.Region = undefined; var command: dma.Region = undefined; -// The scanout backing is a **shared** (shm) region, not DMA: cacheable so the compositor +// The scanout backing is a **shared** (shared-memory) region, not DMA: cacheable so the compositor // composites into it cheaply (x86 DMA is coherent, so the device still sees the writes), and // shareable so the same physical pages the device scans out of are the ones the compositor // paints. The driver keeps the capability to hand to the compositor in the announce. -var surface: shm.Region = undefined; +var surface: shared_memory.Region = undefined; // Split-virtqueue producer/consumer shadows. var avail_shadow: u16 = 0; @@ -376,13 +376,13 @@ fn initialise(endpoint: ipc.Handle) bool { } } - // Back the resource with a shared (shm) surface, so the compositor and the device work + // Back the resource with a shared (shared-memory) surface, so the compositor and the device work // the same physical pages. The device needs the guest-physical base for attach_backing. - surface = shm.create(scanout_bytes) orelse { + surface = shared_memory.create(scanout_bytes) orelse { log("virtio-gpu: scanout surface allocation failed\n", .{}); return false; }; - const surface_physical = shm.physical(surface.handle) orelse { + const surface_physical = shared_memory.physical(surface.handle) orelse { log("virtio-gpu: could not resolve the scanout surface physical address\n", .{}); return false; }; diff --git a/system/kernel/architecture/x86_64/cpu.zig b/system/kernel/architecture/x86_64/cpu.zig index 9bbd7a9..5cfbfeb 100644 --- a/system/kernel/architecture/x86_64/cpu.zig +++ b/system/kernel/architecture/x86_64/cpu.zig @@ -189,8 +189,8 @@ pub fn mapUserDmaInto(root: u64, virtual: u64, physical: u64, len: u64) void { } /// Map shared cacheable RAM into address space `root`: write-back cacheable, RW+NX, and -/// marked so teardown won't free the frames (they're owned by a refcounted shm object, -/// freed when its last capability drops). For shm_create/shm_map. +/// marked so teardown won't free the frames (they're owned by a refcounted shared-memory object, +/// freed when its last capability drops). For shared_memory_create/shared_memory_map. pub fn mapUserSharedInto(root: u64, virtual: u64, physical: u64, len: u64) void { paging.mapUserSharedInto(root, virtual, physical, len); } diff --git a/system/kernel/ipc-synchronous.zig b/system/kernel/ipc-synchronous.zig index 34476bf..1475ae7 100644 --- a/system/kernel/ipc-synchronous.zig +++ b/system/kernel/ipc-synchronous.zig @@ -153,35 +153,35 @@ pub fn dropRef(endpoint: *Endpoint) void { /// The `kind` tag on a `scheduler.HandleObject` — which capability object a handle names. /// Defined here (not in scheduler) because the meaning is the IPC/capability layer's. pub const handle_kind_endpoint: u8 = 0; -pub const handle_kind_shm: u8 = 1; +pub const handle_kind_shared_memory: u8 = 1; /// A page-aligned block of **shared cacheable RAM** (docs/display-v2.md), referenced by /// capability handles across processes and freed when the last one drops. `phys` is its /// contiguous physical base, `pages` its length. A sharer's address-space teardown never /// reclaims these frames (the mapping carries `device_grant`); this object owns them. -pub const ShmObject = struct { +pub const SharedMemoryObject = struct { refcount: u32 = 1, phys: u64, pages: usize, }; /// Wrap `pages` contiguous frames at `phys` (already allocated + zeroed by the caller) in a -/// refcounted shm object, or null if the heap is out of room. -pub fn createShm(phys: u64, pages: usize) ?*ShmObject { - const shm = heap.allocator().create(ShmObject) catch return null; - shm.* = .{ .phys = phys, .pages = pages }; - return shm; +/// refcounted shared-memory object, or null if the heap is out of room. +pub fn createSharedMemory(phys: u64, pages: usize) ?*SharedMemoryObject { + const shared_memory = heap.allocator().create(SharedMemoryObject) catch return null; + shared_memory.* = .{ .phys = phys, .pages = pages }; + return shared_memory; } /// Drop a shared-memory reference; when the last one goes, return its frames to the /// allocator and free the object. (The mappings themselves are torn down with each /// sharer's address space; `device_grant` keeps that from freeing the frames early.) -pub fn dropShmRef(shm: *ShmObject) void { - if (shm.refcount > 1) { - shm.refcount -= 1; +pub fn dropSharedMemoryReference(shared_memory: *SharedMemoryObject) void { + if (shared_memory.refcount > 1) { + shared_memory.refcount -= 1; } else { - for (0..shm.pages) |i| pmm.free(shm.phys + i * page_size); - heap.allocator().destroy(shm); + for (0..shared_memory.pages) |i| pmm.free(shared_memory.phys + i * page_size); + heap.allocator().destroy(shared_memory); } } @@ -294,8 +294,8 @@ fn shareCapability(from: *Task, to: *Task, cap: u64) i64 { const e: *Endpoint = @ptrCast(@alignCast(entry.ptr)); e.refcount += 1; }, - handle_kind_shm => { - const s: *ShmObject = @ptrCast(@alignCast(entry.ptr)); + handle_kind_shared_memory => { + const s: *SharedMemoryObject = @ptrCast(@alignCast(entry.ptr)); s.refcount += 1; }, else => return -EBADF, @@ -512,12 +512,12 @@ pub fn installHandle(t: *Task, endpoint: *Endpoint) i64 { } /// Install a shared-memory handle. -pub fn installShmHandle(t: *Task, shm: *ShmObject) i64 { - return installEntry(t, .{ .kind = handle_kind_shm, .ptr = @ptrCast(shm) }); +pub fn installSharedMemoryHandle(t: *Task, shared_memory: *SharedMemoryObject) i64 { + return installEntry(t, .{ .kind = handle_kind_shared_memory, .ptr = @ptrCast(shared_memory) }); } /// Resolve a handle to its endpoint, or null if out of range, unused, or a different kind -/// (e.g. an shm handle used where an endpoint is expected). +/// (e.g. a shared-memory handle used where an endpoint is expected). pub fn resolveHandle(t: *Task, h: u64) ?*Endpoint { if (h >= t.handles.len) return null; const entry = t.handles[@intCast(h)] orelse return null; @@ -526,11 +526,11 @@ pub fn resolveHandle(t: *Task, h: u64) ?*Endpoint { } /// Resolve a handle to its shared-memory object, or null if out of range, unused, or not -/// an shm handle. -pub fn resolveShm(t: *Task, h: u64) ?*ShmObject { +/// a shared-memory handle. +pub fn resolveSharedMemory(t: *Task, h: u64) ?*SharedMemoryObject { if (h >= t.handles.len) return null; const entry = t.handles[@intCast(h)] orelse return null; - if (entry.kind != handle_kind_shm) return null; + if (entry.kind != handle_kind_shared_memory) return null; return @ptrCast(@alignCast(entry.ptr)); } @@ -550,7 +550,7 @@ pub fn closeHandles(t: *Task) void { fn dropEntry(entry: scheduler.HandleObject) void { switch (entry.kind) { handle_kind_endpoint => dropRef(@ptrCast(@alignCast(entry.ptr))), - handle_kind_shm => dropShmRef(@ptrCast(@alignCast(entry.ptr))), + handle_kind_shared_memory => dropSharedMemoryReference(@ptrCast(@alignCast(entry.ptr))), else => {}, } } diff --git a/system/kernel/process.zig b/system/kernel/process.zig index 6b17970..0e4e6d7 100644 --- a/system/kernel/process.zig +++ b/system/kernel/process.zig @@ -82,17 +82,17 @@ pub const device_arena_end: u64 = device_arena_base + (4 << 30); pub const dma_arena_base: u64 = 0x0000_7200_0000_0000; pub const dma_arena_end: u64 = dma_arena_base + (256 << 20); // 256 MiB per process -/// The shared-memory arena: where `shm_create`/`shm_map` place shared cacheable regions, in +/// The shared-memory arena: where `shared_memory_create`/`shared_memory_map` place shared cacheable regions, in /// PML4[230] — a user-exclusive region distinct from the DMA arena. The frames are owned by -/// a refcounted shm object and freed when its last capability drops, not on teardown, so the -/// mapping carries `device_grant`. Per-process cursor in `Task.shm_map_next` (docs/display-v2.md). -pub const shm_arena_base: u64 = 0x0000_7300_0000_0000; -pub const shm_arena_end: u64 = shm_arena_base + (256 << 20); // 256 MiB per process +/// a refcounted shared-memory object and freed when its last capability drops, not on teardown, so the +/// mapping carries `device_grant`. Per-process cursor in `Task.shared_memory_map_next` (docs/display-v2.md). +pub const shared_memory_arena_base: u64 = 0x0000_7300_0000_0000; +pub const shared_memory_arena_end: u64 = shared_memory_arena_base + (256 << 20); // 256 MiB per process -/// Largest single `shm_create`, in pages (32 MiB) — enough for a 4K framebuffer surface; -/// also an overflow guard on the page count. shm frames are contiguous (like DMA), so this +/// Largest single `shared_memory_create`, in pages (32 MiB) — enough for a 4K framebuffer surface; +/// also an overflow guard on the page count. shared-memory frames are contiguous (like DMA), so this /// bounds the contiguous allocation asked of the frame allocator. -const maximum_shm_pages = 8192; +const maximum_shared_memory_pages = 8192; /// Largest single `mmap` grant, in pages (32 MiB). Big enough for a display service's /// back buffer at up to 4K (3840x2160x4 ≈ 8100 pages); the user heap otherwise grows in @@ -225,9 +225,9 @@ fn system_call(state: *architecture.CpuState) void { .timer_bind => systemTimerBind(state), .klog_read => systemKlogRead(state), .wall_clock => systemWallClock(state), - .shm_create => systemShmCreate(state), - .shm_map => systemShmMap(state), - .shm_physical => systemShmPhysical(state), + .shared_memory_create => systemSharedMemoryCreate(state), + .shared_memory_map => systemSharedMemoryMap(state), + .shared_memory_physical => systemSharedMemoryPhysical(state), .thread_spawn => systemThreadSpawn(state), .current_core => systemCurrentCore(state), .thread_self => systemThreadSelf(state), @@ -513,79 +513,79 @@ fn systemDmaFree(state: *architecture.CpuState) void { architecture.setSystemCallResult(state, 0); } -/// shm_create(len) -> virtual_address (rax), handle (rdx): grant `len` bytes (rounded up to whole +/// shared_memory_create(len) -> virtual_address (rax), handle (rdx): grant `len` bytes (rounded up to whole /// pages) of **shareable, zeroed, cacheable** RAM — contiguous frames mapped into the -/// caller's shm arena — and hand back the virtual address plus a capability handle. Unlike +/// caller's shared-memory arena — and hand back the virtual address plus a capability handle. Unlike /// `dma_alloc` the memory is write-back cacheable (for CPU compositing, not device DMA) and /// its frames are owned by a refcounted object: the handle is passed to another process as -/// an `ipc_call` send_cap, that process `shm_map`s it, and the frames free only when the +/// an `ipc_call` send_cap, that process `shared_memory_map`s it, and the frames free only when the /// last capability drops (docs/display-v2.md — the compositor↔native-driver and /// app↔compositor surface path). -fn systemShmCreate(state: *architecture.CpuState) void { +fn systemSharedMemoryCreate(state: *architecture.CpuState) void { const len = architecture.systemCallArg(state, 0); const t = scheduler.current(); if (t.address_space == 0 or len == 0) return fail(state); const pages: usize = @intCast((len + page_size - 1) / page_size); - if (pages == 0 or pages > maximum_shm_pages) return fail(state); + if (pages == 0 or pages > maximum_shared_memory_pages) return fail(state); // Reserve arena virtual space up front, so a mapping failure needs no rollback. - if (t.shm_map_next == 0) t.shm_map_next = shm_arena_base; - const base_v = t.shm_map_next; - if (base_v + pages * page_size > shm_arena_end) return fail(state); // arena exhausted + if (t.shared_memory_map_next == 0) t.shared_memory_map_next = shared_memory_arena_base; + const base_v = t.shared_memory_map_next; + if (base_v + pages * page_size > shared_memory_arena_end) return fail(state); // arena exhausted const phys = pmm.allocContiguous(pages, ~@as(u64, 0)) orelse return fail(state); // Zero through the physmap (the frames aren't mapped in the caller yet). const kernel_view: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(phys)); @memset(kernel_view[0 .. pages * page_size], 0); - const shm = ipc.createShm(phys, pages) orelse { + const shared_memory = ipc.createSharedMemory(phys, pages) orelse { for (0..pages) |i| pmm.free(phys + i * page_size); return fail(state); }; - const handle = ipc.installShmHandle(t, shm); + const handle = ipc.installSharedMemoryHandle(t, shared_memory); if (handle < 0) { - ipc.dropShmRef(shm); // last ref: frees the object and its frames + ipc.dropSharedMemoryReference(shared_memory); // last ref: frees the object and its frames return fail(state); } architecture.mapUserSharedInto(t.address_space, base_v, phys, pages * page_size); - t.shm_map_next = base_v + pages * page_size; + t.shared_memory_map_next = base_v + pages * page_size; architecture.setSystemCallResult(state, base_v); // virtual_address for the CPU architecture.setSystemCallResult2(state, @intCast(handle)); // capability handle to pass on } -/// shm_map(cap) -> virtual_address: map the shared region named by a capability handle the caller -/// received (via an `ipc_call` send_cap) into its shm arena — the same physical frames the +/// shared_memory_map(cap) -> virtual_address: map the shared region named by a capability handle the caller +/// received (via an `ipc_call` send_cap) into its shared-memory arena — the same physical frames the /// creator sees — returning the virtual address. The handle already holds a reference (taken /// when the capability was shared), so this only adds a mapping; it never bumps the refcount. -fn systemShmMap(state: *architecture.CpuState) void { +fn systemSharedMemoryMap(state: *architecture.CpuState) void { const cap = architecture.systemCallArg(state, 0); const t = scheduler.current(); if (t.address_space == 0) return fail(state); - const shm = ipc.resolveShm(t, cap) orelse return fail(state); // not an shm handle we hold - if (t.shm_map_next == 0) t.shm_map_next = shm_arena_base; - const base_v = t.shm_map_next; - const size = shm.pages * page_size; - if (base_v + size > shm_arena_end) return fail(state); + const shared_memory = ipc.resolveSharedMemory(t, cap) orelse return fail(state); // not a shared-memory handle we hold + if (t.shared_memory_map_next == 0) t.shared_memory_map_next = shared_memory_arena_base; + const base_v = t.shared_memory_map_next; + const size = shared_memory.pages * page_size; + if (base_v + size > shared_memory_arena_end) return fail(state); - architecture.mapUserSharedInto(t.address_space, base_v, shm.phys, size); - t.shm_map_next = base_v + size; + architecture.mapUserSharedInto(t.address_space, base_v, shared_memory.phys, size); + t.shared_memory_map_next = base_v + size; architecture.setSystemCallResult(state, base_v); } -/// shm_physical(cap) -> physical_address: the guest-physical base of a shared region the caller holds a +/// shared_memory_physical(cap) -> physical_address: the guest-physical base of a shared region the caller holds a /// capability for. The frames are contiguous (allocated by `allocContiguous`), so a single /// physical base + length describes the whole region — which is exactly what a driver needs -/// to hand a shm surface to a device (virtio-gpu `attach_backing`). Only a holder of the +/// to hand a shared-memory surface to a device (virtio-gpu `attach_backing`). Only a holder of the /// capability can ask; there is no ambient way to turn a virtual address into a physical one. -fn systemShmPhysical(state: *architecture.CpuState) void { +fn systemSharedMemoryPhysical(state: *architecture.CpuState) void { const cap = architecture.systemCallArg(state, 0); const t = scheduler.current(); if (t.address_space == 0) return fail(state); - const shm = ipc.resolveShm(t, cap) orelse return fail(state); // not an shm handle we hold - architecture.setSystemCallResult(state, shm.phys); + const shared_memory = ipc.resolveSharedMemory(t, cap) orelse return fail(state); // not a shared-memory handle we hold + architecture.setSystemCallResult(state, shared_memory.phys); } /// device_register(parent_id, descriptor_ptr) -> id: publish a child device below a device diff --git a/system/kernel/scheduler.zig b/system/kernel/scheduler.zig index b6ede31..8c2354e 100644 --- a/system/kernel/scheduler.zig +++ b/system/kernel/scheduler.zig @@ -113,7 +113,7 @@ pub const Task = struct { ipc_reply_cap: u64 = 0, ipc_status: i64 = 0, // client: reply length / -errno, written by the replier dma_map_next: u64 = 0, // bump pointer into this task's DMA arena (0 = unseeded) - shm_map_next: u64 = 0, // bump pointer into this task's shared-memory arena (0 = unseeded) + shared_memory_map_next: u64 = 0, // bump pointer into this task's shared-memory arena (0 = unseeded) ipc_send_cap: u64 = ~@as(u64, 0), // handle to transfer with this message (abi.no_cap = none) ipc_received_cap: u64 = ~@as(u64, 0), // client: handle the reply's transferred cap landed at (abi.no_cap = none) next: ?*Task = null, // ready-queue link (also the endpoint sender-FIFO link) diff --git a/system/kernel/tests.zig b/system/kernel/tests.zig index c27f154..e6b38d9 100644 --- a/system/kernel/tests.zig +++ b/system/kernel/tests.zig @@ -103,8 +103,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void { displayDemoTest(boot_information); } else if (eql(case, "display-cursor")) { displayCursorTest(boot_information); - } else if (eql(case, "shm")) { - shmTest(boot_information); + } else if (eql(case, "shared-memory")) { + sharedMemoryTest(boot_information); } else if (eql(case, "virtio-gpu")) { virtioGpuTest(boot_information); } else if (eql(case, "display-native")) { @@ -2860,14 +2860,14 @@ fn displayDemoTest(boot_information: *const BootInformation) void { while (true) scheduler.yield(); } -/// V2 — cross-process shared memory (docs/display-v2.md). Spawn shm-server and shm-client: -/// the client shm_creates a region, writes a pattern, and passes the region's capability to -/// the server as an ipc_call send_cap; the server shm_maps it and confirms the pattern is +/// V2 — cross-process shared memory (docs/display-v2.md). Spawn shared-memory-server and shared-memory-client: +/// the client shared_memory_creates a region, writes a pattern, and passes the region's capability to +/// the server as an ipc_call send_cap; the server shared_memory_maps it and confirms the pattern is /// visible — proving the two processes share the same physical pages, and that the extended -/// capability-passing (endpoints → memory objects) works. Its `shm: shared 4096 bytes ok` +/// capability-passing (endpoints → memory objects) works. Its `shared-memory: shared 4096 bytes ok` /// heartbeat is the marker. -fn shmTest(boot_information: *const BootInformation) void { - log("DANOS-TEST-BEGIN: shm\n", .{}); +fn sharedMemoryTest(boot_information: *const BootInformation) void { + log("DANOS-TEST-BEGIN: shared-memory\n", .{}); if (boot_information.initial_ramdisk_len == 0) { check("bootloader handed over an initial_ramdisk", false); result(); @@ -2880,12 +2880,12 @@ fn shmTest(boot_information: *const BootInformation) void { return; }; - if (!spawnNamed(rd, "shm-server")) { - log("shm: could not spawn shm-server\n", .{}); + if (!spawnNamed(rd, "shared-memory-server")) { + log("shared-memory: could not spawn shared-memory-server\n", .{}); result(); return; } - _ = spawnNamed(rd, "shm-client"); + _ = spawnNamed(rd, "shared-memory-client"); scheduler.setPriority(1); // below the two, so they run while (true) scheduler.yield(); } diff --git a/system/services/display/backend.zig b/system/services/display/backend.zig index b2cb924..a5697e9 100644 --- a/system/services/display/backend.zig +++ b/system/services/display/backend.zig @@ -160,7 +160,7 @@ fn presentSpan(destination: [*]volatile u32, source: [*]const u32, count: usize) pub const Mode = scanout_protocol.Mode; /// The native virtio-gpu backend: the compositor composes into a **shared** scanout surface -/// (an `shm` region the driver created and handed over) and `present` asks the driver to put +/// (a shared-memory region the driver created and handed over) and `present` asks the driver to put /// a frame on the panel over its `.scanout` endpoint. Unlike GOP there is no local copy — the /// surface *is* the device's resource backing, so compositing writes land straight where the /// driver transfers-and-flushes from (x86 DMA is cache-coherent, so the cacheable shared pages diff --git a/system/services/display/display.zig b/system/services/display/display.zig index 3a5639d..480bad8 100644 --- a/system/services/display/display.zig +++ b/system/services/display/display.zig @@ -307,10 +307,10 @@ fn verifyNativePresent() void { fn attachScanout(stride: u32, width: u32, height: u32, format: u32, refresh_hz: u32, capability: ?ipc.Handle, reply: []u8) usize { const cap = capability orelse return fail(reply); if (width == 0 or height == 0 or stride < width) return fail(reply); - const mapped = runtime.shm.map(cap) orelse return fail(reply); + const mapped = runtime.shared_memory.map(cap) orelse return fail(reply); const scanout = ipc.lookup(.scanout) orelse return fail(reply); // A second announce means the driver died and was restarted (V6): re-attach to its fresh - // scanout. (The previous shared mapping leaks — there is no shm_unmap syscall yet — but the + // scanout. (The previous shared mapping leaks — there is no shared_memory_unmap syscall yet — but the // frames are the dead driver's, reclaimed on its exit; a handful across a crash is benign.) const reattach = switch (backend) { .virtio => true, diff --git a/system/services/shm-client/shm-client.zig b/system/services/shared-memory-client/shared-memory-client.zig similarity index 50% rename from system/services/shm-client/shm-client.zig rename to system/services/shared-memory-client/shared-memory-client.zig index f6e2ec6..84dddeb 100644 --- a/system/services/shm-client/shm-client.zig +++ b/system/services/shared-memory-client/shared-memory-client.zig @@ -1,17 +1,17 @@ -//! system/services/shm-client — the creating half of the shm test (docs/display-v2.md V2). -//! It `shm_create`s a shared region, writes a known pattern into it, and hands the region's -//! capability to `shm-server` as an `ipc_call` send_cap. The server maps that capability and +//! system/services/shared-memory-client — the creating half of the shared-memory test (docs/display-v2.md V2). +//! It `shared_memory_create`s a shared region, writes a known pattern into it, and hands the region's +//! capability to `shared-memory-server` as an `ipc_call` send_cap. The server maps that capability and //! confirms the pattern is visible — proving cross-process shared memory over the extended //! capability-passing path. const runtime = @import("runtime"); const system = runtime.system; -const shm = runtime.shm; +const shared_memory = runtime.shared_memory; const ipc = runtime.ipc; const pattern_len = 4096; -/// The pattern the server checks — must match shm-server.zig. +/// The pattern the server checks — must match shared-memory-server.zig. fn expected(i: usize) u8 { return @truncate(i *% 7 +% 3); } @@ -19,28 +19,28 @@ fn expected(i: usize) u8 { fn lookupServer() ?ipc.Handle { var attempts: usize = 0; while (attempts < 100) : (attempts += 1) { - if (ipc.lookup(.shm_test)) |h| return h; + if (ipc.lookup(.shared_memory_test)) |h| return h; system.sleep(50); } return null; } pub fn main() void { - const region = shm.create(pattern_len) orelse { - _ = system.write("shm: create failed\n"); + const region = shared_memory.create(pattern_len) orelse { + _ = system.write("shared-memory: create failed\n"); return; }; var i: usize = 0; while (i < pattern_len) : (i += 1) region.ptr[i] = expected(i); const server = lookupServer() orelse { - _ = system.write("shm: no server\n"); + _ = system.write("shared-memory: no server\n"); return; }; - // A non-empty message (so it reaches on_message, not the ping path), carrying the shm + // A non-empty message (so it reaches on_message, not the ping path), carrying the shared-memory // region's capability. The reply is empty; we just need the round trip. var reply: [64]u8 = undefined; - _ = ipc.callCap(server, "shm", &reply, region.handle) catch { - _ = system.write("shm: call failed\n"); + _ = ipc.callCap(server, "shared-memory", &reply, region.handle) catch { + _ = system.write("shared-memory: call failed\n"); }; } diff --git a/system/services/shared-memory-server/shared-memory-server.zig b/system/services/shared-memory-server/shared-memory-server.zig new file mode 100644 index 0000000..c73e0e9 --- /dev/null +++ b/system/services/shared-memory-server/shared-memory-server.zig @@ -0,0 +1,44 @@ +//! system/services/shared-memory-server — the receiving half of the shared-memory test (docs/display-v2.md V2). +//! It registers under `ServiceId.shared_memory_test`; when `shared-memory-client` calls it carrying a +//! shared-memory capability, it `shared_memory_map`s that capability and checks the client's pattern +//! is visible through the mapping — proving the two processes share the same physical pages +//! (not a copy). On success it prints `shared-memory: shared 4096 bytes ok`, the test's marker. + +const runtime = @import("runtime"); +const system = runtime.system; +const shared_memory = runtime.shared_memory; +const ipc = runtime.ipc; + +const pattern_len = 4096; + +/// The pattern the client writes — must match shared-memory-client.zig. +fn expected(i: usize) u8 { + return @truncate(i *% 7 +% 3); +} + +fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize { + _ = message; + _ = reply; + _ = sender; + const cap = capability orelse { + _ = system.write("shared-memory: shared FAILED (no capability)\n"); + return 0; + }; + const ptr = shared_memory.map(cap) orelse { + _ = system.write("shared-memory: shared FAILED (map)\n"); + return 0; + }; + var i: usize = 0; + while (i < pattern_len) : (i += 1) { + if (ptr[i] != expected(i)) { + _ = system.write("shared-memory: shared FAILED (mismatch)\n"); + return 0; + } + } + _ = system.write("shared-memory: shared 4096 bytes ok\n"); + return 0; // empty reply — the client only needs the round trip to unblock +} + +pub fn main() void { + runtime.service.run(64, .{ .service = .shared_memory_test, .on_message = onMessage }); +} diff --git a/system/services/shm-server/shm-server.zig b/system/services/shm-server/shm-server.zig deleted file mode 100644 index 335fc1e..0000000 --- a/system/services/shm-server/shm-server.zig +++ /dev/null @@ -1,44 +0,0 @@ -//! system/services/shm-server — the receiving half of the shm test (docs/display-v2.md V2). -//! It registers under `ServiceId.shm_test`; when `shm-client` calls it carrying a -//! shared-memory capability, it `shm_map`s that capability and checks the client's pattern -//! is visible through the mapping — proving the two processes share the same physical pages -//! (not a copy). On success it prints `shm: shared 4096 bytes ok`, the test's marker. - -const runtime = @import("runtime"); -const system = runtime.system; -const shm = runtime.shm; -const ipc = runtime.ipc; - -const pattern_len = 4096; - -/// The pattern the client writes — must match shm-client.zig. -fn expected(i: usize) u8 { - return @truncate(i *% 7 +% 3); -} - -fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize { - _ = message; - _ = reply; - _ = sender; - const cap = capability orelse { - _ = system.write("shm: shared FAILED (no capability)\n"); - return 0; - }; - const ptr = shm.map(cap) orelse { - _ = system.write("shm: shared FAILED (map)\n"); - return 0; - }; - var i: usize = 0; - while (i < pattern_len) : (i += 1) { - if (ptr[i] != expected(i)) { - _ = system.write("shm: shared FAILED (mismatch)\n"); - return 0; - } - } - _ = system.write("shm: shared 4096 bytes ok\n"); - return 0; // empty reply — the client only needs the round trip to unblock -} - -pub fn main() void { - runtime.service.run(64, .{ .service = .shm_test, .on_message = onMessage }); -} diff --git a/test/qemu_test.py b/test/qemu_test.py index 0ae317d..a5b3a63 100644 --- a/test/qemu_test.py +++ b/test/qemu_test.py @@ -195,12 +195,12 @@ CASES = [ "smp": 4, "expect": r"display: online \d+x\d+[\s\S]*display: cursor tracking mouse ok", "fail": r"display: (could not|mouse subscribe failed)|CPU EXCEPTION|KERNEL PANIC"}, - # Shared memory (v2 V2): shm-client creates a region, writes a pattern, and passes its - # capability to shm-server, which maps it and confirms the same bytes — proving + # Shared memory (v2 V2): shared-memory-client creates a region, writes a pattern, and passes its + # capability to shared-memory-server, which maps it and confirms the same bytes — proving # cross-process shared pages over the extended capability passing. - {"name": "shm", - "expect": r"shm: shared 4096 bytes ok", - "fail": r"shm: (shared FAILED|create failed|no server|call failed|map)|CPU EXCEPTION|KERNEL PANIC"}, + {"name": "shared-memory", + "expect": r"shared-memory: shared 4096 bytes ok", + "fail": r"shared-memory: (shared FAILED|create failed|no server|call failed|map)|CPU EXCEPTION|KERNEL PANIC"}, # virtio-gpu driver (v2 V3): boot with an emulated virtio-gpu. The device-manager stack # discovers the PCI function and spawns the driver, which brings up the control virtqueue, # creates a 2D scanout resource backed by DMA memory, set_scanouts it, paints a test