Files
vatrog-vm-signaling/include/vmsig_event.h
T

281 lines
16 KiB
C
Raw Normal View History

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