#ifndef VMSIG_EVENT_H #define VMSIG_EVENT_H #include #include /* vmsig_event.h — neutral "transfer event" + "payload" model. * * This is the ONLY type that crosses the signaling core. The taxonomy names the * transfer SEMANTICS, not neighbor types: a TU compiled against this header * cannot name vmctl_batch, vgpu_producer_t, or vmie_mem. The SI data body lives * in an opaque payload owned by the source adapter's codec; the core does NOT * dereference it — it only routes the event and carries the payload. */ /* Transfer direction relative to control. */ typedef enum { VMSIG_DIR_UP = 0, /* sensor/state -> control */ VMSIG_DIR_DOWN = 1 /* control decision -> actuation/SI */ } vmsig_dir; /* Logical seam (SI role) the event crosses. NEUTRAL roles, not driver names: * assigned at adapter registration, used only for routing, the priority default, * and the subscription filter. */ typedef enum { VMSIG_SRC_NONE = 0, VMSIG_SRC_FRAME = 1, /* vgpu desktop sensor role; reserved: no signaling adapter, * the future vgpu-perception shell-as-control carries it (CURSOR_STATE) */ VMSIG_SRC_INPUT = 2, /* input/actuation + lifecycle (vmctl role) */ VMSIG_SRC_CONTROL = 3, /* originated by a control endpoint */ VMSIG_SRC_CORE = 4, /* core-internal (shutdown/error/tick) */ VMSIG_SRC_VMHOST = 5, /* VM substrate / QEMU: lifecycle + events (own QMP) */ VMSIG_SRC_MEMCTX = 6, /* coherent guest address-space context (kcr3+locator) */ VMSIG_SRC_MAX } vmsig_source; /* Delivery priority class. Higher value — earlier delivery. This is NOT a * behavioral timing but ordering on the "wire". The default is assigned per * source at registration; the emitter may override it per event. */ typedef enum { VMSIG_PRIO_BULK = 0, /* frames, large state deltas */ VMSIG_PRIO_NORMAL = 1, /* routine ack/samples */ VMSIG_PRIO_HIGH = 2, /* input commands (latency-sensitive) */ VMSIG_PRIO_URGENT = 3, /* lifecycle, seam-down, errors */ VMSIG_PRIO_MAX } vmsig_prio; /* NEUTRAL event taxonomy: each kind is a transfer MEANING that exactly one * adapter codec decodes from / encodes into its contract. The core routes by * kind + source + dir + prio and does not interpret the payload. */ typedef enum { /* --- generic / lifecycle (any seam) --- */ VMSIG_EV_NONE = 0, VMSIG_EV_SEAM_UP = 1, /* SI seam came up (attach/bootstrap ok) */ VMSIG_EV_SEAM_DOWN = 2, /* seam lost (heartbeat stale, socket closed) */ VMSIG_EV_ERROR = 3, /* adapter/core error, details in payload */ /* (16..19 — retired STATE_* of the MEMSTATE seam; do NOT reuse numbers: on a * version skew an old STATE kind must not alias a new kind on the wire.) */ /* (32..36 — retired FRAME_READY/FRAME_STATE/BULK_ATTACHED/BULK_READY/BULK_DETACHED of * the removed FRAME adapter + bulk data-plane (vgpu perception moved to an S-lib); * do NOT reuse numbers — wire-skew safety.) */ /* --- UP: cursor (vgpu sensor; emitted by the vgpu-perception shell-as-control) --- */ VMSIG_EV_CURSOR_STATE = 37, /* cursor position/visibility; inln=vmsig_cursor; cap OBSERVE|INPUT */ /* --- UP: input/lifecycle ack (INPUT seam) --- */ VMSIG_EV_ACT_ACK = 48, /* down-command completed (ok/err) */ VMSIG_EV_VM_LIFECYCLE = 49, /* power/lifecycle state report */ /* --- UP: lease arbitration (all addressed, origin=initiator; source=CORE) --- */ VMSIG_EV_LEASE_GRANTED = 50, /* lease granted (CMD_ACQUIRE succeeded) */ VMSIG_EV_LEASE_DENIED = 51, /* lease denied (reason in vmsig_lease_req) */ VMSIG_EV_LEASE_RELEASED= 52, /* lease released by owner (CMD_RELEASE) */ VMSIG_EV_LEASE_REVOKED = 53, /* lease taken away by preemption/death */ VMSIG_EV_LEASE_STATUS = 54, /* response to CMD_LEASE_STATUS (vmsig_lease_status) */ /* --- UP: response to a held-input query (INPUT seam, addressed to initiator) --- */ VMSIG_EV_INPUT_HELD = 55, /* set of held KEY/BTN from the vmctl record; inln=vmsig_input_held */ /* --- DOWN: control decisions --- */ VMSIG_EV_CMD_INPUT = 64, /* input injection (abs/rel/btn/key/scroll) */ VMSIG_EV_CMD_LIFECYCLE = 65, /* powerdown/reset/wakeup/pause/resume */ /* (66 — retired CMD_STREAM of the removed FRAME adapter; the future vgpu-control * down-path returns via write-signaled/MEMWRITE. 67..69 — retired * CMD_QUERY/WATCH/UNWATCH; do NOT reuse numbers.) */ VMSIG_EV_CMD_VM = 70, /* base VM control (vmsig_vm_cmd; VMHOST seam) */ /* (71..72 — retired CMD_SUBSCRIBE_BULK/UNSUBSCRIBE_BULK of the bulk data-plane; * do NOT reuse numbers.) */ /* --- DOWN: lease arbitration (intercepted by the core, not forwarded to the adapter) --- */ VMSIG_EV_CMD_ACQUIRE = 73, /* request an exclusive lease of a class: inln=vmsig_lease_req */ VMSIG_EV_CMD_RELEASE = 74, /* release your own lease of a class: inln=vmsig_lease_req */ VMSIG_EV_CMD_LEASE_STATUS = 75, /* query lease status of a class: inln=vmsig_lease_req */ VMSIG_EV_CMD_QUERY_INPUT = 76, /* query held KEY/BTN (from the vmctl record); reply UP INPUT_HELD; cap INPUT */ /* --- UP: address-space context (MEMCTX seam; coherent kcr3+locator datum) --- */ VMSIG_EV_MEMCTX = 77, /* context multicast/replay: inln=vmsig_memctx, * payload=vmsig_memseg[] (owned), RO-fd alongside */ VMSIG_EV_MEMCTX_INVALIDATED = 78, /* epoch invalidation: inln=vmsig_memctx_inv (URGENT) */ /* --- DOWN: coherent memory write (write-signaled; MEMCTX seam) --- */ VMSIG_EV_CMD_MEMWRITE = 79, /* atomic gva_write under the held lease; inln=vmsig_memwrite (+tail/payload bytes); * cap MEMWRITE + lease MEMWRITE + extent. ACK via ACT_ACK{ok,corr}. */ VMSIG_EV_KIND_MAX } vmsig_kind; /* ===== Lease arbitration (exclusive-ownership layer for destructive resources) ===== * A destructive VM resource is owned by EXACTLY one control (per endpoint+class pair). * The class is generic; INPUT, POWER and MEMWRITE are active. MEMWRITE is the * write-signaled atomic guest-memory write on the MEMCTX seam. */ typedef enum { VMSIG_LEASE_INPUT = 0, /* exclusive grab of input (CMD_INPUT) */ VMSIG_LEASE_POWER = 1, /* exclusive destructive power (lifecycle/VM) */ VMSIG_LEASE_MEMWRITE = 2, /* exclusive atomic guest-memory write (gva_write); NO finalization */ VMSIG_LEASE_CLASS_MAX } vmsig_lease_class; /* Lease denial reason (vmsig_lease_req.reason in UP LEASE_DENIED). */ enum { VMSIG_LEASE_DENY_HELD = 0, /* held by an equal/higher; the owner holds it */ VMSIG_LEASE_DENY_NOCAP = 1, /* no cap for the class (CAP_INPUT/CAP_POWER) */ VMSIG_LEASE_DENY_NOGRANT = 2, /* endpoint outside the grant (endpoint_mask) */ VMSIG_LEASE_DENY_BADCLASS = 3, /* class out of range */ VMSIG_LEASE_DENY_LOWER_PRIO = 4 /* contender priority not above the owner's */ }; /* Lease request/response (DOWN CMD_ACQUIRE/RELEASE/LEASE_STATUS and UP LEASE_*, in inln). */ typedef struct { uint32_t cls; /* vmsig_lease_class */ uint32_t reason; /* DOWN: 0; UP LEASE_DENIED: VMSIG_LEASE_DENY_* */ } vmsig_lease_req; /* Response to CMD_LEASE_STATUS (UP LEASE_STATUS, in inln). */ typedef struct { uint32_t cls; /* requested class */ uint32_t busy; /* 1=held by a live owner, 0=free */ uint32_t owner_principal; /* owner principal (for audit/UI); 0 if free */ } vmsig_lease_status; /* Lifecycle operations for CMD_LIFECYCLE (code in inln[0]). Destructive ones * (POWERDOWN/RESET) require CAP_POWER; safe ones — CAP_LIFECYCLE. */ enum { VMSIG_LIFE_POWERDOWN = 0, VMSIG_LIFE_RESET = 1, VMSIG_LIFE_WAKEUP = 2, VMSIG_LIFE_PAUSE = 3, VMSIG_LIFE_RESUME = 4 }; /* ===== Input (DOWN VMSIG_EV_CMD_INPUT, in inln) — NEUTRAL ===== * control describes input abstractly (axis/button/key/scroll), WITHOUT knowing the driver * (uinput/QMP): the input adapter translates it into its contract. Requires CAP_INPUT. This * is the ONLY public input-encoding contract — an external control encodes vmsig_input into * vmsig_event.inln. */ typedef enum { VMSIG_INPUT_ABS = 0, /* absolute axis: code=axis, value=coordinate */ VMSIG_INPUT_REL = 1, /* relative axis: code=axis, value=delta */ VMSIG_INPUT_BTN = 2, /* button: code=button, value=pressed(1)/released(0) */ VMSIG_INPUT_KEY = 3, /* key: code=evdev code, value=pressed/released */ VMSIG_INPUT_SCROLL = 4 /* scroll: code=axis, scroll=magnitude */ } vmsig_input_kind; typedef struct { uint32_t kind; /* vmsig_input_kind */ int32_t code; /* axis / button / evdev code (neutral event code) */ int32_t value; /* abs coordinate / rel delta / pressed(1)|released(0) */ double scroll; /* scroll magnitude (VMSIG_INPUT_SCROLL only) */ } vmsig_input; /* fits in vmsig_event.inln[48] */ /* ===== Memory write (DOWN VMSIG_EV_CMD_MEMWRITE) — NEUTRAL, write-signaled ===== * control describes an ATOMIC write into guest memory abstractly (guest VA + length), * WITHOUT knowing vmie/cr3: the memctx adapter resolves it under the held kcr3 and does * ONE gva_write. Requires CAP_MEMWRITE + an exclusive MEMWRITE lease + an extent check. * SRC bytes: inline (<= VMSIG_MEMWRITE_INLINE) ride in the inln tail right after this header * (flags & INLINE); larger in-proc writes ride in the borrowed payload (flags & PAYLOAD). */ #define VMSIG_MEMWRITE_INLINE 32u /* inln tail capacity for SRC (48 - 16 header) */ #define VMSIG_MW_SRC_INLINE 0x1u /* SRC bytes are in inln tail (len<=INLINE) */ #define VMSIG_MW_SRC_PAYLOAD 0x2u /* SRC bytes are in ev->payload.data (in-proc) */ typedef struct { uint64_t gva; /* guest virtual address to write (resolved under the adapter's kcr3) */ uint32_t len; /* number of bytes to write (1..VMSIG_MEMWRITE_MAX) */ uint32_t flags; /* VMSIG_MW_SRC_INLINE | VMSIG_MW_SRC_PAYLOAD */ /* inline SRC tail (when VMSIG_MW_SRC_INLINE): up to VMSIG_MEMWRITE_INLINE bytes follow */ } vmsig_memwrite; /* header = 8+4+4 = 16 bytes; +32 tail = 48 (exactly inln[48]) */ /* ===== Cursor (UP VMSIG_EV_CURSOR_STATE, in inln) — NEUTRAL ===== * Cursor position from the SCREEN sensor (vgpu). NEUTRAL payload format only: emitted by the * out-of-repo vgpu-perception shell-as-control (source VMSIG_SRC_FRAME), not by a signaling * adapter — signaling just fans it out. x,y signed (multi-monitor -> negative). cap OBSERVE|INPUT. */ typedef struct { int32_t x; /* screen coordinate X (signed) */ int32_t y; /* screen coordinate Y (signed) */ uint32_t visible; /* 1=shown, 0=hidden */ uint32_t seq; /* monotonic cursor-publication counter (vgpu) */ } vmsig_cursor; /* ===== Held input (UP VMSIG_EV_INPUT_HELD, in inln) — response to CMD_QUERY_INPUT ===== * Set of held KEY/BTN from the ACTUATOR record (vmctl): signaling only returns it on request, * does NOT track it itself and does NOT decide release (that is control). flags & TRUNC => more * held than ent. */ #define VMSIG_INPUT_HELD_TRUNC 0x1u typedef struct { uint32_t count; /* number of valid entries in ent[] */ uint32_t flags; /* VMSIG_INPUT_HELD_TRUNC if more held than capacity */ struct { uint16_t kind; uint16_t code; } ent[10]; /* kind=VMSIG_INPUT_KEY/BTN; code */ } vmsig_input_held; /* 4+4+10*4 = 48 (exactly inln[48]) */ /* ===== QEMU/QMP host-plane (VMHOST seam) — VM-substrate control ===== * VM state (UP VMSIG_EV_VM_LIFECYCLE, in inln). */ enum { VMSIG_VM_RUNNING = 0, VMSIG_VM_PAUSED, VMSIG_VM_SHUTDOWN, VMSIG_VM_RESET, VMSIG_VM_POWERDOWN, VMSIG_VM_CRASHED, VMSIG_VM_UNKNOWN }; typedef struct { uint32_t state; uint32_t detail; } vmsig_vm_state; /* VM control operations (DOWN VMSIG_EV_CMD_VM, in inln). Destructive ones * (RESET/POWERDOWN/QUIT) require CAP_POWER; safe ones — CAP_VM. */ enum { VMSIG_VMOP_QUERY = 0, /* query-status */ VMSIG_VMOP_CONT, /* cont (resume) */ VMSIG_VMOP_STOP, /* stop (pause) */ VMSIG_VMOP_RESET, /* system_reset (destructive) */ VMSIG_VMOP_POWERDOWN, /* system_powerdown (destructive) */ VMSIG_VMOP_QUIT /* quit (destructive) */ }; typedef struct { uint32_t op; } vmsig_vm_cmd; /* Codec tags: which adapter owns the payload body (for release/diagnostics). */ typedef enum { VMSIG_CODEC_NONE = 0, VMSIG_CODEC_INPUT = 1, VMSIG_CODEC_VMHOST = 2, VMSIG_CODEC_MEMCTX = 3 /* owned-payload locator (vmsig_memseg[]) of the MEMCTX seam */ } vmsig_codec; /* Payload ownership flags. */ #define VMSIG_PL_OWNED 0x1u /* core frees it via release() on drop */ #define VMSIG_PL_BORROWED 0x2u /* borrowed (e.g. a seqlock frame): copy */ /* or revalidate before release() */ #define VMSIG_PL_INLINE 0x4u /* small body lives in vmsig_event.inln */ /* Opaque, releasable payload. The body is owned by the emitting adapter's codec * (mmap'd frame slot, vmie heap diff, ...). The core carries the bearer and calls * release() EXACTLY once on consumption/drop. The core never dereferences data. */ typedef struct vmsig_payload { void* data; /* opaque body, codec-defined */ size_t len; /* bytes in data (0 if borrowed) */ uint32_t codec; /* vmsig_codec: whose payload it is */ uint32_t flags; /* VMSIG_PL_* */ void (*release)(struct vmsig_payload*); /* idempotent; may be NULL */ void* owner; /* codec context for release() */ } vmsig_payload; /* TRANSFER EVENT. Fixed-size header + a small inline zone; large bodies hang off * the payload. */ typedef struct vmsig_event { vmsig_kind kind; vmsig_source source; /* source seam */ vmsig_dir dir; vmsig_prio prio; uint32_t endpoint; /* VM/endpoint id — multi-VM-ready */ uint32_t seq; /* monotonic sequence (set by the context) */ uint32_t corr; /* correlation: links an ACK to its CMD */ uint32_t origin; /* INTERNAL: id+1 of the control that initiated DOWN (0=none/broadcast). */ /* Set by the core in emit_down; NOT serialized onto the wire */ /* (a poller cannot forge it). Addressed reply delivery. */ uint64_t ts_ns; /* CLOCK_MONOTONIC at emit time */ vmsig_payload payload; /* opaque body (may be empty) */ uint8_t inln[48]; /* inline zone for small events (VMSIG_PL_INLINE) */ } vmsig_event; /* Release the event's payload (if it has release and is not yet freed). Idempotent. */ static inline void vmsig_payload_release(vmsig_event* ev) { if (ev && ev->payload.release) { ev->payload.release(&ev->payload); ev->payload.release = NULL; } } #endif /* VMSIG_EVENT_H */