Merge branch 'claude/shm-runtime-meaning-4fce7c'

# Conflicts:
#	build.zig
#	system/drivers/virtio-gpu/virtio-gpu.zig
This commit is contained in:
Daniel Samson 2026-07-21 17:27:39 +01:00
commit 0a4388c3bc
No known key found for this signature in database
GPG Key ID: A9EB2589C60F7268
24 changed files with 191 additions and 191 deletions

View File

@ -541,8 +541,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
@ -611,8 +611,8 @@ pub fn build(b: *std.Build) void {
.{ .path = "system/drivers/pci-bus", .binary = pci_bus_exe.getEmittedBin() },
.{ .path = "system/tests/vfs-test", .binary = vfstest_exe.getEmittedBin() },
.{ .path = "system/tests/fat-test", .binary = fat_test_exe.getEmittedBin() },
.{ .path = "system/tests/shm-server", .binary = shm_server_exe.getEmittedBin() },
.{ .path = "system/tests/shm-client", .binary = shm_client_exe.getEmittedBin() },
.{ .path = "system/tests/shared-memory-server", .binary = shared_memory_server_exe.getEmittedBin() },
.{ .path = "system/tests/shared-memory-client", .binary = shared_memory_client_exe.getEmittedBin() },
.{ .path = "system/tests/crash-test", .binary = crash_test_exe.getEmittedBin() },
.{ .path = "system/tests/device-list", .binary = device_list_exe.getEmittedBin() },
.{ .path = "system/tests/input-source", .binary = input_source_exe.getEmittedBin() },

View File

@ -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 +

View File

@ -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.

View File

@ -1,7 +1,7 @@
# The display service v2: a pluggable scanout backend
**Status: complete (V1V6).** 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

View File

@ -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

View File

@ -7,7 +7,7 @@ System calls (syscalls) are the bridge between your programs and the operating s
> dispatcher); the `int 0x80` gate is kept alongside as a minimal test path.
> The live table is `system/abi.zig` (private, renumberable — see
> [vdso.md](vdso.md) for the public boundary): process lifecycle + threads,
> memory (mmap/dma/shm), synchronous + async IPC with capability passing,
> memory (mmap/dma/shared-memory), synchronous + async IPC with capability passing,
> device access, time, the tagged-log diagnostics (`debug_write` with a level,
> `klog_read`/`klog_status`), and filesystem NAMING (`fs_resolve`/`fs_node`/
> `fs_mount`/`fs_unmount` — the kernel VFS root routes paths and serves the

View File

@ -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.

View File

@ -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

View File

@ -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` |

View File

@ -39,9 +39,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.

View File

@ -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
//! compositornative-driver and appcompositor 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;
}

View File

@ -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 tagged log-ring stream bytes from `offset` out to a user buffer; fails once `offset` falls behind the ring's tail (re-sync via klog_status)
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)
@ -291,7 +291,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)
_,
};

View File

@ -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;
@ -369,13 +369,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 {
std.log.info("scanout surface allocation failed", .{});
return false;
};
const surface_physical = shm.physical(surface.handle) orelse {
const surface_physical = shared_memory.physical(surface.handle) orelse {
std.log.info("could not resolve the scanout surface physical address", .{});
return false;
};

View File

@ -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);
}

View File

@ -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,8 +512,8 @@ 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) });
}
/// Install an endpoint handle, reusing an existing slot that already names this
@ -531,7 +531,7 @@ pub fn installHandleDeduped(t: *Task, endpoint: *Endpoint) i64 {
}
/// 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;
@ -540,11 +540,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));
}
@ -564,7 +564,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 => {},
}
}

View File

@ -83,17 +83,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
@ -242,9 +242,9 @@ fn system_call(state: *architecture.CpuState) void {
.fs_mount => systemFsMount(state),
.fs_unmount => systemFsUnmount(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),
@ -530,79 +530,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 compositornative-driver and
/// appcompositor 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

View File

@ -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)

View File

@ -106,8 +106,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")) {
@ -2894,14 +2894,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();
@ -2914,12 +2914,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();
}

View File

@ -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

View File

@ -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,

View File

@ -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");
};
}

View File

@ -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 });
}

View File

@ -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 });
}

View File

@ -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