naming: shm → shared_memory — 'shm' is a Unix clipping, not an acronym

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.
This commit is contained in:
Daniel Samson
2026-07-21 17:26:15 +01:00
parent ec6e888076
commit 0045fd87ba
23 changed files with 192 additions and 192 deletions
+4 -4
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 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)
_,
};
+6 -6
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;
@@ -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;
};
+2 -2
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);
}
+21 -21
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,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 => {},
}
}
+38 -38
View File
@@ -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
+1 -1
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)
+11 -11
View File
@@ -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();
}
+1 -1
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
+2 -2
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,
@@ -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");
};
}
@@ -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 });
}
-44
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 });
}