mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-20 19:06:37 +03:00
102 lines
5.8 KiB
C
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 */
|