#ifndef VMSIG_MEMCTX_H #define VMSIG_MEMCTX_H #include #include #include "vmsig_event.h" /* vmsig_memctx.h — NEUTRAL handoff contract for the guest address-space context. * * signaling is a COHERENCE layer for shared state, not perception. Over memory it * vends ONE coherent datum: the root of the guest address space (the permanent System * DirectoryTableBase, `kcr3`) PAIRED with a RAM-region locator — a pre-opened `O_RDONLY` * fd. The holder (an S library / any control, including a human operator via their shim) * subscribes to this datum, opens ITS OWN read-only context FROM the received fd (keyed on * `kcr3`), and does proc_list/gva_read/scan/pmap itself. Perception and semantics are NOT here. * * Holder invariants: * - The locator is valid ONLY against the received `O_RDONLY` fd. From it the holder opens * its own read-only context, keyed on the vended `kcr3`: * * raw reads under a cr3 it already holds — vmie_mem_from_ro_fd(fd, low) (nseg==0) or * vmie_mem_from_ro_fd_segs(fd, segs, nseg) (nseg>0); gva_read keyed on (mem, kcr3); * * FULL read context WITH process/module discovery — vmie_win32_open_ro_fd(fd, low, * kcr3): builds the offset profile read-only from the image (no beacon/ACK) and * enables proc_list/proc_modules plus the section/import/export/scan surfaces. A * sensor that must FIND a process (then read its private AS) needs this one — kcr3 * alone gives reads-under-a-known-cr3, not discovery. * Both map PROT_READ (gva_write -> -1). `kcr3` is valid ONLY within its `epoch`. * - On UP MEMCTX_INVALIDATED{endpoint,epoch} the holder closes its context/fd-mmap * and waits for the next MEMCTX{epoch+1} (re-multicast with a new kcr3 and a fresh fd). * - The fd is always `O_RDONLY` (VMSIG_MEMCTX_RDONLY set by this layer): mmap(PROT_WRITE) * through it -> EACCES. Writing into the guest is structurally impossible on the holder * side — it goes through the write-signaled MEMWRITE command (CMD_MEMWRITE under the * MEMWRITE lease), never this RO mapping. */ /* Locator-POD flag: the region is vended read-only (always set by signaling). */ #define VMSIG_MEMCTX_RDONLY 0x1u /* Address-space context locator-POD (rides in vmsig_event.inln; <=48 bytes). * Flat self-describing encoding: nseg explicit, no offset magic. */ typedef struct { uint64_t kcr3; /* permanent System DirectoryTableBase (guest AS root) */ uint64_t low; /* below-4G RAM size (PCI-hole split point; single-low open) */ uint32_t epoch; /* VM-session epoch; kcr3 valid ONLY within it */ uint32_t nseg; /* number of segments in the owned-payload (0 => single-low by `low`) */ uint32_t flags; /* VMSIG_MEMCTX_RDONLY */ uint32_t _pad; } vmsig_memctx; /* 8+8+4+4+4+4 = 32 bytes */ /* One GPA->file segment (mirrors the neighbor's gpa_seg from memmodel.h, but self-contained: * this header does NOT pull in the neighbor's contract). Rides in the owned-payload of the * MEMCTX event when nseg>0. For a single-low image nseg==0 and the holder opens by `low`. */ typedef struct { uint64_t gpa; /* GPA of the window */ uint64_t len; /* window length in bytes */ uint64_t file_off; /* offset into the RAM-backing file */ } vmsig_memseg; /* Epoch invalidation (UP VMSIG_EV_MEMCTX_INVALIDATED, in inln). */ typedef struct { uint32_t endpoint; uint32_t epoch; /* new epoch; the previous one's context is invalid */ } vmsig_memctx_inv; /* Decode the MEMCTX event's owned-payload into segs[] (pointer + nseg). A pure function over * the event: no ownership, no allocations. Returns a pointer to the segments (or NULL, setting * *out_nseg=0, if there are none — e.g. a single-low image OR socket delivery, where the * payload does not cross the wire and the holder opens by `low`). */ static inline const vmsig_memseg* vmsig_memctx_segs(const vmsig_event* ev, uint32_t* out_nseg) { const vmsig_memctx* m = (const vmsig_memctx*)ev->inln; uint32_t n = m->nseg; if (!n || !ev->payload.data || ev->payload.len < (size_t)n * sizeof(vmsig_memseg)) { if (out_nseg) *out_nseg = 0; return NULL; } if (out_nseg) *out_nseg = n; return (const vmsig_memseg*)ev->payload.data; } /* ===== Registration seam adapter -> core ===== * * The memctx adapter registers THIS in the core via vmsig_emit.register_memctx. The core * keeps the registration per-endpoint (retained-context) and does NOT store a copy of the * locator: on delivery/replay it calls describe() (current locator snapshot) + share_fd() * (fresh O_RDONLY fd). The epoch is stamped by the CORE (single source of truth); describe * does NOT fill it. invalidate() — the core asks the adapter to re-bootstrap on an epoch * change (the adapter re-emits MEMCTX once ready). All callbacks are called on the loop * thread. ctx — the adapter's private context. */ typedef struct vmsig_memctx_reg { uint32_t endpoint; uint32_t source; /* VMSIG_SRC_MEMCTX */ void* ctx; /* adapter's private context */ /* Current locator snapshot: kcr3/low/nseg/flags + segs (borrowed, owned by the * adapter; lives across epochs). The core overwrites epoch with its own value. */ void (*describe)(void* ctx, vmsig_memctx* out_pod, const vmsig_memseg** out_segs, uint32_t* out_nseg); int (*share_fd)(void* ctx); /* fresh O_RDONLY fd of the RAM region (caller closes) */ void (*invalidate)(void* ctx, uint32_t epoch); /* re-bootstrap for the new epoch */ } vmsig_memctx_reg; #endif /* VMSIG_MEMCTX_H */