#ifndef VGPU_PERCEPTION_INTERNAL_H #define VGPU_PERCEPTION_INTERNAL_H /* perception-internal.h — private consumer-side helpers (NOT a public surface). * * Holds the core's private state type, the consumer-side seqlock read discipline * (the mirror of the producer's atomic-shim accessors, but an independent body — * we read into local copies via gva_read, never sharing producer code), the * structural-invariant validator table used by discovery, and the bit unpackers * for the packed cursor fields. Included only by the perception TUs. * * Consumer seqlock discipline: every guest read goes through gva_read into a * local copy, so the compiler cannot reorder a data read across the seq read — * each gva_read is an opaque call. We still bump the seq read into its own * gva_read and treat odd seq / changed seq as "writer in flight → retry". */ #include #include #include #include "vgpu_stream.h" #include "memmodel.h" #include "vgpu_perception.h" /* Bounded seqlock retry. Producer windows are short (a single slot publish), so * a small count suffices; spinning longer would be a behavioural timing choice * (control's job), which does not belong in the sensor. Exhausted → lossy skip. */ #define VGPUP_SEQLOCK_RETRIES 8u /* Private core state. Owns nothing of the address space — only where the region * lives (in the producer's user-AS, keyed by proc_cr3) and the last-seen * monotonic markers for dedup / session-break. */ struct vgpup_region { uint64_t proc_cr3; /* producer process cr3 — key to its user-AS */ uint64_t region_gva; /* producer-block GVA == region base */ uint64_t ctrl_gva; /* region_gva + VGPU_CONTROL_OFFSET (cached) */ uint64_t ring_gva; /* region_gva + VGPU_RING_OFFSET (cached) */ uint64_t last_frame_id; /* dedup: only frames with a greater id are "fresh" */ uint32_t run_epoch; /* last run_epoch seen via vgpup_read_status */ }; /* Per-cr3 user-AS region scan (discovery steps 3–5 for ONE address space): scan * gva_regions over [USER_MIN, USER_MAX] under `cr3` for a contiguous RW run of * >= VGPU_REGION_BYTES, read the producer block at its base, and accept it iff * the structural-invariant table holds. On the first hit writes the region base * GVA to *out_region_gva and the heartbeat snapshot to *out_hb0 and returns 0; * <0 if none is found / a read fails. Pure gva_* (no proc_list / win32) so it is * testable under a synthetic cr3; vgpup_discover_candidate calls it per process. */ int vgpup_scan_user_as_for_region(vmie_mem* m, uint64_t cr3, uint64_t* out_region_gva, uint64_t* out_hb0); /* ---- seqlock primitives -------------------------------------------------- */ static inline int vgpup_seq_is_writing(uint32_t seq) { return (seq & 1u) != 0u; } /* Read one 32-bit seq field at `gva` into *out under `cr3` (the producer's * user-AS cr3). 0 on success, <0 on read error. */ static inline int vgpup_read_seq(vmie_mem* m, uintptr_t cr3, uint64_t gva, uint32_t* out) { return gva_read(m, cr3, (uintptr_t)gva, out, sizeof *out) < 0 ? -1 : 0; } /* ---- packed-field unpackers (cursor line) -------------------------------- */ static inline int32_t vgpup_cursor_x(uint64_t pos) { return (int32_t)(uint32_t)(pos & 0xFFFFFFFFu); } static inline int32_t vgpup_cursor_y(uint64_t pos) { return (int32_t)(uint32_t)(pos >> 32); } static inline uint16_t vgpup_lo16(uint32_t v) { return (uint16_t)(v & 0xFFFFu); } static inline uint16_t vgpup_hi16(uint32_t v) { return (uint16_t)(v >> 16); } /* ---- structural-invariant validator (discovery, BY TABLE — no magic) ------ * * Discovery has no magic field in the ABI (the owner forbids one). The * discriminator is the conjunction of structural invariants derived from the * ABI bounds in vgpu_stream.h, plus the two-phase heartbeat liveness handled by * the caller. The predicates run cheap→costly with early exit; each takes a * decoded producer-block snapshot and returns 1 (holds) / 0 (rejects). */ typedef int (*vgpup_inv_fn)(const vgpu_producer_t* p); /* Is `latest` a valid slot index, or the legitimate "no frame yet" sentinel? * latest == NONE is NOT a rejection (a freshly-started region has no frame). */ static inline int vgpup_inv_latest_in_range(const vgpu_producer_t* p) { return p->latest == VGPU_LATEST_NONE || p->latest < VGPU_SLOT_COUNT; } /* If a frame is published, its slot seq must be even (stable, not mid-write). */ static inline int vgpup_inv_latest_seq_stable(const vgpu_producer_t* p) { if (p->latest == VGPU_LATEST_NONE) { return 1; } return !vgpup_seq_is_writing(p->seq[p->latest]); } /* If a frame is published, its descriptor must be a tight BGRA frame within the * ABI dimension bounds. */ static inline int vgpup_inv_latest_desc_valid(const vgpu_producer_t* p) { const vgpu_desc_t* d; if (p->latest == VGPU_LATEST_NONE) { return 1; } d = &p->desc[p->latest]; if (d->format != VGPU_FMT_BGRA8888) { return 0; } if (d->width == 0u || d->width > VGPU_MAX_WIDTH) { return 0; } if (d->height == 0u || d->height > VGPU_MAX_HEIGHT) { return 0; } if (d->stride != d->width * 4u) { return 0; } return 1; } /* Cold-line status enum must be in the ABI range. */ static inline int vgpup_inv_status_in_range(const vgpu_producer_t* p) { return p->status <= VGPU_ST_ERROR; } /* Cold-line backend enum must be in the ABI range. */ static inline int vgpup_inv_backend_in_range(const vgpu_producer_t* p) { return p->backend <= VGPU_BK_GDI; } /* The producer must advertise the one wire format we consume. */ static inline int vgpup_inv_supports_bgra(const vgpu_producer_t* p) { return (p->supported_formats & (1u << VGPU_FMT_BGRA8888)) != 0u; } /* The invariant table, cheap→costly. A candidate is accepted (phase 1) iff * every predicate holds; the table is the single discriminator, no scattered * ifs and no hardcoded numbers (all bounds come from vgpu_stream.h). */ static const vgpup_inv_fn VGPUP_INVARIANTS[] = { vgpup_inv_latest_in_range, vgpup_inv_status_in_range, vgpup_inv_backend_in_range, vgpup_inv_supports_bgra, vgpup_inv_latest_seq_stable, vgpup_inv_latest_desc_valid, }; #define VGPUP_INVARIANT_COUNT (sizeof(VGPUP_INVARIANTS) / sizeof(VGPUP_INVARIANTS[0])) /* Run the whole invariant table over a decoded producer-block snapshot. * Returns 1 if every predicate holds, 0 on the first rejection. */ static inline int vgpup_invariants_hold(const vgpu_producer_t* p) { size_t i; for (i = 0; i < VGPUP_INVARIANT_COUNT; ++i) { if (!VGPUP_INVARIANTS[i](p)) { return 0; } } return 1; } #endif /* VGPU_PERCEPTION_INTERNAL_H */