mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-26 04:36:37 +03:00
fix(discovery): derive the below-4G split robustly from fragmented mtree
host_probe derived the guest's below-4G split (vmie `low`) by taking the first GPA-0 RAM run in `info mtree -f`. When low RAM is fragmented by overlay pages (Hyper-V SynIC) and blackhole holes (smbase/tseg), that first run is a tiny fragment, so the split came out far too small and host_bootstrap could never recover the System DTB — the memctx context was never published. Extract a pure parser, mtree_low_split(): anchor on the system flatview, take `low` from the @file-offset of the high-RAM region at GPA >= 4 GiB (which equals the split by construction), cross-validate against the PCI-hole base, and fail closed when it can't be derived. QMP-reply un-escaping moves to the transport boundary so the parser works on plain text. Unit-tested against a synthetic fragmented flatview including a decoy non-system address space. postinst also hints to restart the daemon after an upgrade (a running instance keeps the old build until restarted). Bump 0.3.6.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/* test_mtree.c — unit tests for mtree_low_split (the below-4G split parser). Pure text in,
|
||||
* number out; no QMP/transport. The fragmented fixture reproduces the structural traps the
|
||||
* old heuristic tripped on (Hyper-V synic overlays, smbase/tseg blackhole holes, rom holes)
|
||||
* plus a decoy non-system flatview that carries its OWN GPA-0 stub and a DIFFERENT @offset,
|
||||
* proving the system address space is selected (not "first match in the text"). */
|
||||
#define _GNU_SOURCE
|
||||
#include "mtree.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef FIXTURE_DIR
|
||||
#define FIXTURE_DIR "."
|
||||
#endif
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, msg) do { if (!(cond)) { printf(" FAIL: %s\n", (msg)); g_fail = 1; } } while (0)
|
||||
|
||||
/* Slurp a whole text file into a heap buffer (NUL-terminated). NULL on error. */
|
||||
static char* slurp(const char* path) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return NULL;
|
||||
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; }
|
||||
long sz = ftell(f);
|
||||
if (sz < 0) { fclose(f); return NULL; }
|
||||
rewind(f);
|
||||
char* buf = malloc((size_t)sz + 1);
|
||||
if (!buf) { fclose(f); return NULL; }
|
||||
size_t got = fread(buf, 1, (size_t)sz, f);
|
||||
fclose(f);
|
||||
buf[got] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Case B: a minimal, NON-fragmented system flatview — one big GPA-0 ram run plus high-RAM
|
||||
* carrying @<low>. Must not be broken by the new parser. */
|
||||
static const char* k_happy =
|
||||
"FlatView #0\n"
|
||||
" AS \"memory\", root: system\n"
|
||||
" Root memory region: system\n"
|
||||
" 0000000000000000-000000007fffffff (prio 0, ram): ram0\n"
|
||||
" 0000000080000000-0000000081ffffff (prio 0, i/o): vfio-pci-bar3\n"
|
||||
" 0000000100000000-000000017fffffff (prio 0, ram): ram0 @0000000080000000\n";
|
||||
|
||||
/* Case C: text without any system flatview => fail-closed. */
|
||||
static const char* k_no_system =
|
||||
"FlatView #0\n"
|
||||
" AS \"I/O\", root: io\n"
|
||||
" Root memory region: io\n"
|
||||
" 0000000000000000-0000000000000007 (prio 0, i/o): dma-chan\n";
|
||||
|
||||
int main(void) {
|
||||
printf("test_mtree\n");
|
||||
|
||||
/* Cases A and E: the fragmented fixture (decoy first, system second). */
|
||||
char path[1024];
|
||||
snprintf(path, sizeof path, "%s/mtree_split_fragmented.txt", FIXTURE_DIR);
|
||||
char* frag = slurp(path);
|
||||
CHECK(frag != NULL, "fragmented fixture loaded");
|
||||
if (frag) {
|
||||
uint64_t low = mtree_low_split(frag);
|
||||
/* A: fragmented low-RAM must NOT yield the GPA-0 stub end (0x18000) — the bug. */
|
||||
CHECK(low == 0x80000000ull, "A: fragmented split == 0x80000000");
|
||||
CHECK(low != 0x18000ull, "A: not the GPA-0 stub end (0x18000)");
|
||||
/* E: the decoy (non-system) flatview comes FIRST and carries @0x40000000; the
|
||||
* function must select the SYSTEM flatview (@0x80000000), not the decoy. */
|
||||
CHECK(low != 0x40000000ull, "E: decoy flatview @offset rejected (system AS chosen)");
|
||||
free(frag);
|
||||
}
|
||||
|
||||
/* Case B: happy path (non-fragmented) still resolves to the high-RAM @offset. */
|
||||
CHECK(mtree_low_split(k_happy) == 0x80000000ull, "B: non-fragmented happy path == 0x80000000");
|
||||
|
||||
/* Case C: no system flatview => 0. */
|
||||
CHECK(mtree_low_split(k_no_system) == 0, "C: no system flatview => fail-closed 0");
|
||||
|
||||
/* Case D: garbage / empty => 0. */
|
||||
CHECK(mtree_low_split("") == 0, "D: empty text => 0");
|
||||
CHECK(mtree_low_split("not an mtree at all\n") == 0, "D: junk text => 0");
|
||||
|
||||
printf("mtree tests: %s\n", g_fail ? "FAIL" : "PASS");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user