129 lines
4.2 KiB
C
129 lines
4.2 KiB
C
|
|
/*
|
||
|
|
* spoof-core.c — seed/config acquisition + the deterministic derivation engine
|
||
|
|
* + the policy knobs (hv mode / WAET / vmgenid). See spoof-core.h, spoof.h.
|
||
|
|
*/
|
||
|
|
#include "qemu/osdep.h"
|
||
|
|
#include "qemu/cutils.h" /* pstrcpy */
|
||
|
|
#include "hw/misc/spoof.h"
|
||
|
|
#include "hw/misc/spoof-core.h"
|
||
|
|
#include "hw/core/boards.h" /* current_machine */
|
||
|
|
#include "qom/object.h"
|
||
|
|
|
||
|
|
const char SPOOF_A36[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||
|
|
const char SPOOF_LET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||
|
|
const char SPOOF_DIG[] = "0123456789";
|
||
|
|
|
||
|
|
/* ---- config: machine property first, env var as a test fallback ----------- */
|
||
|
|
static char g_seed[256];
|
||
|
|
static bool g_ready, g_enabled;
|
||
|
|
static char g_hv[16], g_waet[16], g_vgid[16], g_pvp[16];
|
||
|
|
|
||
|
|
static void read_prop(const char *prop, const char *env, char *out, size_t n)
|
||
|
|
{
|
||
|
|
out[0] = '\0';
|
||
|
|
if (current_machine && object_property_find(OBJECT(current_machine), prop)) {
|
||
|
|
char *v = object_property_get_str(OBJECT(current_machine), prop, NULL);
|
||
|
|
if (v) { pstrcpy(out, n, v); g_free(v); }
|
||
|
|
}
|
||
|
|
if (!out[0]) {
|
||
|
|
const char *e = getenv(env);
|
||
|
|
if (e) { pstrcpy(out, n, e); }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void config_load(void)
|
||
|
|
{
|
||
|
|
if (g_ready) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
read_prop("spoof-seed", "QEMU_SPOOF_SEED", g_seed, sizeof(g_seed));
|
||
|
|
read_prop("spoof-hv", "QEMU_SPOOF_HV", g_hv, sizeof(g_hv));
|
||
|
|
read_prop("spoof-waet", "QEMU_SPOOF_WAET", g_waet, sizeof(g_waet));
|
||
|
|
read_prop("spoof-vmgenid", "QEMU_SPOOF_VMGENID", g_vgid, sizeof(g_vgid));
|
||
|
|
read_prop("spoof-pvpanic", "QEMU_SPOOF_PVPANIC", g_pvp, sizeof(g_pvp));
|
||
|
|
g_enabled = g_seed[0] != '\0';
|
||
|
|
g_ready = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool spoof_on(void) { config_load(); return g_enabled; }
|
||
|
|
bool spoof_enabled(void) { return spoof_on(); }
|
||
|
|
|
||
|
|
/* ---- deterministic derivation: fnv1a(seed|key) -> splitmix64 -------------- */
|
||
|
|
static uint64_t fnv1a(const char *s)
|
||
|
|
{
|
||
|
|
uint64_t h = 1469598103934665603ULL;
|
||
|
|
for (; *s; s++) { h ^= (uint8_t)*s; h *= 1099511628211ULL; }
|
||
|
|
return h;
|
||
|
|
}
|
||
|
|
static uint64_t mix(uint64_t x)
|
||
|
|
{
|
||
|
|
x += 0x9E3779B97F4A7C15ULL;
|
||
|
|
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
|
||
|
|
x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
|
||
|
|
return x ^ (x >> 31);
|
||
|
|
}
|
||
|
|
uint64_t spoof_field(const char *key)
|
||
|
|
{
|
||
|
|
config_load();
|
||
|
|
return mix(fnv1a(g_seed) ^ (fnv1a(key) * 0x100000001B3ULL));
|
||
|
|
}
|
||
|
|
uint64_t spoof_field_n(const char *key, unsigned i)
|
||
|
|
{
|
||
|
|
char k[96];
|
||
|
|
snprintf(k, sizeof(k), "%s#%u", key, i);
|
||
|
|
return spoof_field(k);
|
||
|
|
}
|
||
|
|
const char *spoof_pick(const char *key, const char *const *arr, size_t n)
|
||
|
|
{
|
||
|
|
return arr[spoof_field(key) % n];
|
||
|
|
}
|
||
|
|
void spoof_gen(const char *key, const char *cs, int len, char *out)
|
||
|
|
{
|
||
|
|
size_t m = strlen(cs);
|
||
|
|
for (int i = 0; i < len; i++) {
|
||
|
|
out[i] = cs[spoof_field_n(key, (unsigned)i) % m];
|
||
|
|
}
|
||
|
|
out[len] = '\0';
|
||
|
|
}
|
||
|
|
/* one cpu-vendor draw anchors BOTH the CPU signature and the chipset/PCI vendor. */
|
||
|
|
int spoof_anchor_vendor(void)
|
||
|
|
{
|
||
|
|
return (int)(spoof_field("anchor.cpu") & 1); /* 0 Intel, 1 AMD */
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ---- policy knobs --------------------------------------------------------- */
|
||
|
|
SpoofHvMode spoof_hv_mode(void)
|
||
|
|
{
|
||
|
|
config_load();
|
||
|
|
if (!strcmp(g_hv, "hidden")) return SPOOF_HV_HIDDEN;
|
||
|
|
if (!strcmp(g_hv, "hyperv")) return SPOOF_HV_HYPERV;
|
||
|
|
if (!strcmp(g_hv, "off")) return SPOOF_HV_OFF;
|
||
|
|
return g_enabled ? SPOOF_HV_HYPERV : SPOOF_HV_OFF; /* seeded default = hyperv */
|
||
|
|
}
|
||
|
|
bool spoof_waet_drop(void)
|
||
|
|
{
|
||
|
|
config_load();
|
||
|
|
if (!strcmp(g_waet, "on")) return true;
|
||
|
|
if (!strcmp(g_waet, "off")) return false;
|
||
|
|
return g_enabled; /* seeded default = drop */
|
||
|
|
}
|
||
|
|
bool spoof_pvpanic_hide(void)
|
||
|
|
{
|
||
|
|
config_load();
|
||
|
|
if (!strcmp(g_pvp, "on")) return true;
|
||
|
|
if (!strcmp(g_pvp, "off")) return false;
|
||
|
|
return g_enabled; /* seeded default = hide pvpanic */
|
||
|
|
}
|
||
|
|
SpoofVgidPolicy spoof_vmgenid_policy(void)
|
||
|
|
{
|
||
|
|
config_load();
|
||
|
|
if (!strcmp(g_vgid, "hide")) return SPOOF_VGID_HIDE;
|
||
|
|
if (!strcmp(g_vgid, "mask")) return SPOOF_VGID_MASK;
|
||
|
|
if (!strcmp(g_vgid, "keep")) return SPOOF_VGID_KEEP;
|
||
|
|
switch (spoof_hv_mode()) { /* default tracks hv mode */
|
||
|
|
case SPOOF_HV_HIDDEN: return SPOOF_VGID_HIDE;
|
||
|
|
case SPOOF_HV_HYPERV: return SPOOF_VGID_MASK;
|
||
|
|
default: return SPOOF_VGID_KEEP;
|
||
|
|
}
|
||
|
|
}
|