Merge branch 'claude/shm-runtime-meaning-4fce7c'
# Conflicts: # build.zig # system/drivers/virtio-gpu/virtio-gpu.zig
This commit is contained in:
+38
-38
@@ -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 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
|
||||
|
||||
Reference in New Issue
Block a user