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>
This commit is contained in:
2026-06-20 18:46:31 +03:00
commit 709f4b586a
36 changed files with 5820 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
#ifndef VMSIG_CONTROL_H
#define VMSIG_CONTROL_H
#include "vmsig_event.h"
/* vmsig_control.h — control-agnostic seam. Control (an algorithm OR a human)
* attaches via ONE neutral interface: a command queue (down) + an event
* subscription (up). In-process implements the vtable with direct callbacks
* (fd = -1); out-of-process is a socket whose fd is registered with the core like
* any source. The core treats both the same. Orchestration is NOT wired in here —
* only the seam. */
/* Subscription filter: which UP events the control WANTS. This is only a
* NARROWING; the real ceiling is set by the grant (effective = sub ∩ grant). */
typedef struct {
uint32_t source_mask; /* bit (1u<<vmsig_source) for the seam of interest */
vmsig_prio prio_min; /* drop UP below this priority */
uint64_t endpoint_mask; /* 0 = all VMs; otherwise bit (1ull<<endpoint) */
} vmsig_sub;
/* ===== Security layer: a control's grant (capability set) =====
* Neutral ceiling of a poller's rights: which VMs, which UP sources, which classes
* of DOWN commands. The enforcement mechanism is in the core (admission/pump_up/
* emit_down); the policy (who gets what) is set by the embedding program/
* orchestrator. Default DENY: an empty grant => not a valid poller (receives and
* sends nothing). */
#define VMSIG_CAP_OBSERVE 0x1u /* UP of SEAM/generic coherent state (observation) */
#define VMSIG_CAP_INPUT 0x2u /* CMD_INPUT */
/* (0x4 is the freed bit of the removed CAP_STREAM; the future vgpu-control down-path
* returns via write-signaled/MEMWRITE. Do NOT reuse.) */
#define VMSIG_CAP_LIFECYCLE 0x8u /* CMD_LIFECYCLE safe ones (pause/resume/wakeup) */
/* (0x10 is the freed bit of the removed CAP_MEMREAD; do NOT reuse: a stale grant
* with this bit must not silently alias to the privileged memory cap.) */
#define VMSIG_CAP_POWER 0x20u /* destructive lifecycle/VM (powerdown/reset/quit) */
#define VMSIG_CAP_VM 0x40u /* CMD_VM safe ones (query/cont/stop), VMHOST seam */
#define VMSIG_CAP_MEMCTX 0x80u /* SUBSCRIPTION to a coherent AS context (UP MEMCTX*, re-share RO-fd).
* NOT an access broker (that is OS-DAC on the fd) — gates RECEIVING the datum. */
#define VMSIG_CAP_MEMWRITE 0x100u /* CMD_MEMWRITE: atomic write-signaled mutation of shared guest memory
* (separate from the freed CAP_MEMREAD bit — read != write; fresh bit
* avoids stale-grant aliasing to this privileged cap). */
typedef struct {
uint32_t principal; /* id for auditing (uid/token) */
uint64_t endpoint_mask; /* which VMs (bit 1ull<<endpoint, endpoint<64); 0=none */
uint32_t source_mask; /* which UP sources (bit 1u<<vmsig_source) */
uint32_t cap_mask; /* VMSIG_CAP_* */
uint32_t arb_prio; /* lease arbitration priority: higher=stronger; supervisor=max. */
/* Separate from vmsig_prio (on-wire ordering). The default */
/* arbitration policy compares it (STRICTLY higher preempts, ties=owner). */
} vmsig_grant;
/* ===== Lease arbitration policy (orchestrator; §5) =====
* Signaling owns the MECHANISM (exclusivity, preemption, fencing, finalization);
* the POLICY (preempt or deny on conflict) is set by the orchestrator via a
* pluggable callback. Default (cb==NULL) = arb_prio comparison: STRICTLY higher ->
* PREEMPT, otherwise DENY. */
typedef enum {
VMSIG_ARB_DENY = 0, /* deny the contender, the owner keeps it */
VMSIG_ARB_PREEMPT = 1 /* take it from the owner, give it to the contender (QUEUE — reserved) */
} vmsig_arb_decision;
/* Called ONLY when (endpoint,class) is held by a LIVE owner (incumbent) and an
* ACQUIRE arrives from another contender. incumbent/contender are the parties'
* grants (live, not copies); incumbent is NEVER NULL (a dead owner is treated as a
* free slot and policy is not called). Called on the loop thread. */
typedef vmsig_arb_decision (*vmsig_arb_policy)(void* ud, uint32_t endpoint, uint32_t cls,
const vmsig_grant* incumbent,
const vmsig_grant* contender);
/* Control endpoint vtable. The core calls deliver() for UP; control sends DOWN via
* the emit hook that the core installs in set_emit_down(). */
typedef struct vmsig_control_ops {
const char* name;
/* fd for an out-of-process control (socket). -1 => in-process, callbacks only
* (no registration in epoll). */
int (*fd)(void* ctl);
/* Declare interest (called once at attach). */
int (*subscribe)(void* ctl, vmsig_sub* out);
/* Core -> control: an UP event for the subscriber. For in-process, a direct
* call; for socket-control, serialization onto the wire. Borrowed: whatever
* must outlive the call must be copied. */
int (*deliver)(void* ctl, const vmsig_event* ev);
/* Core -> control (socket only): the control-fd is readable; the implementation
* parses the wire into DOWN events and calls the installed down-emit. */
int (*on_readable)(void* ctl);
/* The core installs the hook by which control sends DOWN commands; the core
* routes them into vmsig_ctx_submit(ctx, VMSIG_DIR_DOWN, ev). */
void (*set_emit_down)(void* ctl, int (*emit)(void* token, vmsig_event*),
void* token);
void (*close)(void* ctl);
/* Core -> control: deliver a coherent address-space context (UP MEMCTX) + RO-fd
* of the RAM region. Socket: a vmsig_wire frame (kind=MEMCTX, inln=vmsig_memctx) + fd in cmsg
* (SCM_RIGHTS); the segs payload does NOT go on the wire (the holder opens
* via `low`). In-proc: direct fd + event (segs in payload, decode with vmsig_memctx_segs).
* The fd is BORROWED for the duration of the call (the core closes it afterwards) — the holder
* dup's/mmap's it to keep it. Optional: NULL => control does not accept MEMCTX. 0/-1. */
int (*attach_memctx)(void* ctl, const vmsig_event* ev, int fd);
} vmsig_control_ops;
/* Reference in-process control: a thin shim turning a C callback into a vtable, for
* embedding an algorithm directly. */
typedef struct {
int (*on_event)(void* user, const vmsig_event* up); /* core -> algorithm */
void* user;
vmsig_sub sub; /* subscription filter */
/* Core -> algorithm: a coherent AS context (UP MEMCTX) + RO-fd as a direct int. The fd
* is borrowed (dup/mmap to keep it). NULL => does not accept. 0/-1. */
int (*on_memctx)(void* user, const vmsig_event* ev, int fd);
} vmsig_inproc_cfg;
/* Create a reference in-proc control over cfg (which is copied). Returns an opaque
* ctl for vmsig_core_add_control(core, vmsig_inproc_control_ops(), ctl). Freed via
* ops->close(ctl). NULL on OOM. */
const vmsig_control_ops* vmsig_inproc_control_ops(void);
void* vmsig_inproc_control_new(const vmsig_inproc_cfg* cfg);
/* Send a DOWN command from an in-proc control (after attach). 0 — ok, -1 — error. */
int vmsig_inproc_send(void* ctl, vmsig_event* down);
#endif /* VMSIG_CONTROL_H */