Files
vatrog-vm-signaling/include/vmsig_memctx.h
T
lirent 709f4b586a vmsig: a neutral signaling layer between sensors/input and controls
An epoll-driven, neutral transfer-event bus that connects sensors and input
actuators to one or more controls, bidirectionally. It owns the transfer context
and events — delivery order, priority, protocol-level timing, and an
interrupt-driven event model over fd sources (eventfd/timerfd/sockets) — and
stays agnostic to both the sensor/input drivers and the control.

What lives here:
- memctx: a coherent address-space context per endpoint — the guest address-space
  root paired with a pre-opened read-only RAM-region fd, with per-endpoint epoch
  invalidation and retained replay to late subscribers. Perception lives in
  out-of-tree sensor libraries that consume this datum read-only.
- exclusive-ownership leases for destructive resource classes (input, power,
  memory-write).
- write-signaled memory writes (MEMWRITE): an atomic write to guest memory routed
  through the seam under an exclusive lease, never a writable mapping.
- a host-management seam for VM lifecycle/status and a neutral input-injection
  command path.
- multi-VM endpoints; capability-gated, audited control authorization over an
  in-process or unix-socket transport.

Builds against headers only by default (a stub mode that exercises the seam
without a VM); armed builds link the real sensor/input libraries behind flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 18:46:31 +03:00

102 lines
5.8 KiB
C

#ifndef VMSIG_MEMCTX_H
#define VMSIG_MEMCTX_H
#include <stdint.h>
#include <stddef.h>
#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 */