Files
Snapintosh/tools/test-guard-step.py
Acelogic f47befb905 probes: split xnuboot.c into ordered parts
Phase 1 of decomposing the 15,770-line monitor unit: move contiguous
top-level ranges verbatim into probes/xnuboot/*.c, and have
probes/xnuboot.c #include them in their original order. The compiler
still sees one translation unit. Every #include of a header stays in
probes/xnuboot.c, because quoted includes resolve beside the including
file and the monitor build has no -Icompat.

Cuts fall only between top-level declarations; no function, struct,
initializer or conditional is split, and every part has balanced
#if/#endif. Two exceptions are recorded for phase 2:
- monitor_call_impl is 1,318 lines on its own, so
  probes/xnuboot/sptm_dispatch.c is 1,370 lines.
- The 1,894-line #if SNAP_FINE_LOCKS region is split into three
  consecutive identical #if SNAP_FINE_LOCKS blocks (fine_locks_*.c).
  SNAP_FINE_LOCKS is not defined or undefined inside the region, and
  nothing inside it includes a header.
The parts that carry the m1n1-derived SPTM endpoint numbers and return
codes (report_layout.c, sptm_dispatch.c) keep the file's notice.

Tools that read xnuboot.c as text now read the whole unit through
tools/source_text.py read_source(); tools that hash or pin monitor
sources add every part through source_files() (generic over each .c
in their lists, so later splits are covered the same way).

Verification (base 84dd208 vs this commit, clang 23.1.3):
- clang -c, qualification flags (b12v20 qualification.json, no -flto)
  plus -I shadow -I q1n1/platform/uefi: probes/xnuboot.c objects
  byte-identical (cmp). PASS
- clang -c, probes/run.py FLAGS plus the same -I: byte-identical. PASS
- Qualification flags with -flto: llvm-dis output equal; the full IR
  differs only in 468 inline-asm !srcloc source-location cookies. PASS
- probes/run.py build('xnuboot'), every unit linked: xnuboot.bin and
  xnuboot.elf byte-identical with the qualification flags (LTO,
  bin sha256 0d8b9ec3...) and with run.py FLAGS (2b5d5f6b...). PASS
- Reconstruction: expanding the includes and dropping the part headers
  and the duplicated #if/#endif lines reproduces xnuboot.c exactly.
- Host battery (63 commands: the two gcc SPTM suites, test-idregs,
  test-audit-sptm-refs and every tools/test-*.py that reads these
  sources, plus the edited tools), 300 s limit, base vs split:
  no new failure. 76 of 78 outcomes equal; test-audit-sparse and
  test-monitor-perf timed out (NOT RUN) on split under host load and
  on rerun match base (PASS; FAIL with the same record). Per-class
  reruns of the long suites (21 commands) equal on both trees.
  Pre-existing failures (missing private inputs or stale checks) are
  unchanged.
- wc -l: xnuboot.c 918, every part under 1,000 except sptm_dispatch.c
  (1,370).
2026-10-23 04:04:44 -04:00

184 lines
6.8 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""Exercise the monitor's actual guard-step transition with host stage-2 slots.
Only the hardware operations are mocked. Compile the production guard lookup
and step transition together with the real exception mask implementation.
"""
from pathlib import Path
import os
import subprocess
import tempfile
from source_text import read_source
ROOT = Path(__file__).resolve().parents[1]
def function(source, signature):
start = source.index(signature)
body = source.index('{', start)
depth = 1
end = body + 1
while depth:
depth += (source[end] == '{') - (source[end] == '}')
end += 1
return source[start:end]
HARNESS = r'''
#include "exception.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define GUARD_EXEC 0
#define GUARD_DATA 1
#define S2_XN (1UL << 54)
#define PT_VALID 1UL
struct guard { uint64_t pa, slot, descriptor, mode; };
struct vcpu {
struct guard guards[3];
uint64_t guard_count, open_guards;
struct exception_step stepping;
};
struct context {
struct vcpu v;
uint64_t slots[3], spsr, mdscr, pending, irq, flushes, rearm, disarm;
};
static unsigned checks;
#define CHECK(x) do { checks++; if (!(x)) { \
fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #x); exit(1); } } while (0)
static struct vcpu *vcpu_of(struct context *c) { return &c->v; }
static void guard_write(struct guard *g, int open)
{
*(uint64_t *)g->slot = open ? g->descriptor :
g->mode == GUARD_EXEC ? g->descriptor | S2_XN : g->descriptor & ~PT_VALID;
}
static void guard_open(struct context *c, struct guard *g)
{
guard_write(g, 1); c->v.open_guards++; c->flushes++;
}
static void guards_close(struct context *c)
{
if (!c->v.open_guards) return;
for (unsigned i = 0; i < c->v.guard_count; i++) guard_write(&c->v.guards[i], 0);
c->v.open_guards = 0; c->flushes++;
}
static void step_arm(struct context *c, int on)
{
if (on) c->rearm++; else c->disarm++;
exception_step_set(&c->v.stepping, on, &c->spsr, &c->mdscr);
c->irq = exception_step_fiq(&c->v.stepping, c->pending);
}
/* PRODUCTION */
static void init(struct context *c, uint64_t d)
{
*c = (struct context){0};
c->v.guard_count = 3; c->spsr = 0xa04001c5 | d; c->mdscr = 0x8000;
c->pending = 1;
for (unsigned i = 0; i < 3; i++) {
c->v.guards[i] = (struct guard){
.pa = (i + 1) * 0x1000, .slot = (uintptr_t)&c->slots[i],
.descriptor = (i + 1) * 0x1000 | 0x7ff,
.mode = i == 2 ? GUARD_DATA : GUARD_EXEC};
guard_write(&c->v.guards[i], 0);
}
}
static int opened(struct context *c, unsigned i)
{
return c->slots[i] == c->v.guards[i].descriptor;
}
int main(void)
{
for (unsigned masked = 0; masked < 2; masked++) {
struct context c;
init(&c, masked << 9);
uint64_t original = c.spsr;
guard_step(&c, 0); /* failed translation cannot retain a page */
CHECK(!c.v.stepping.active && !c.flushes && c.spsr == original);
guard_step(&c, 0x1000);
CHECK(c.flushes == 1 && opened(&c, 0) && c.v.open_guards == 1);
uint64_t disarm = c.disarm;
for (unsigned n = 0; n < 1024; n++) {
/* Hardware consumes SS; each continuation must rearm it. */
c.spsr &= ~(1UL << 21);
guard_step(&c, 0x1000 + 4 * (n % 1024));
CHECK(c.flushes == 1 && c.disarm == disarm && c.v.open_guards == 1);
CHECK(c.v.stepping.active && (c.spsr & (1UL << 21)) && !c.irq);
CHECK(c.v.stepping.saved_d == (original & (1UL << 9)));
}
guard_step(&c, 0x2000); /* cross into a different guarded page */
CHECK(c.flushes == 3 && !opened(&c, 0) && opened(&c, 1));
CHECK(c.v.open_guards == 1 && !c.irq);
guard_step(&c, 0x4000); /* branch out: reclose and restore guest D */
CHECK(c.flushes == 4 && !opened(&c, 1) && !c.v.open_guards);
CHECK(c.spsr == original && c.mdscr == 0x8000 && c.irq);
/* A data access inside an EXEC page must close DATA after one step. */
guard_step(&c, 0x1000);
guard_open(&c, &c.v.guards[2]);
step_arm(&c, 1);
uint64_t before = c.flushes;
guard_step(&c, 0x1004);
CHECK(c.flushes == before + 2 && opened(&c, 0) && !opened(&c, 2));
CHECK(c.v.open_guards == 1 && !c.irq);
/* Duplicate opens and a second EXEC page cannot take the shortcut. */
for (unsigned i = 0; i < 2; i++) {
guard_open(&c, &c.v.guards[i]);
before = c.flushes;
guard_step(&c, 0x1008);
CHECK(c.flushes == before + 2 && c.v.open_guards == 1);
CHECK(opened(&c, 0) && !opened(&c, 1) && !opened(&c, 2));
}
/* A changed descriptor or suspended step requires full restoration. */
c.slots[0] |= S2_XN;
before = c.flushes;
guard_step(&c, 0x100c);
CHECK(c.flushes == before + 2 && opened(&c, 0));
step_arm(&c, 0);
before = c.flushes;
guard_step(&c, 0x1010);
CHECK(c.flushes == before + 2 && c.v.stepping.active);
guard_step(&c, 0);
CHECK(c.spsr == original && c.irq);
/* DATA alone is never retained, including a PC on that same page. */
for (unsigned i = 0; i < 2; i++) {
guard_open(&c, &c.v.guards[2]);
step_arm(&c, 1);
before = c.flushes;
guard_step(&c, i ? 0x3000 : 0x4000);
CHECK(c.flushes == before + 1 && !opened(&c, 2));
CHECK(!c.v.open_guards && !c.v.stepping.active && c.spsr == original);
}
}
/* Private stage-2 views must retain/close independently for two CPUs. */
struct context a, b;
init(&a, 0); init(&b, 1UL << 9);
guard_step(&a, 0x1000); guard_step(&b, 0x2000);
guard_step(&a, 0x1004); guard_step(&b, 0x4000);
CHECK(a.flushes == 1 && opened(&a, 0) && a.v.open_guards == 1);
CHECK(b.flushes == 2 && !opened(&b, 1) && !b.v.open_guards);
printf("PASS: %u guard transition and interrupt-mask checks\n", checks);
}
'''
def main():
source = read_source(ROOT / 'probes/xnuboot.c')
production = '\n'.join(function(source, signature) for signature in (
'static struct guard *guard_find(', 'static void guard_step('))
with tempfile.TemporaryDirectory(prefix='snapintosh-guard-step-') as tmp:
c = Path(tmp) / 'guard-step.c'
exe = Path(tmp) / 'guard-step'
c.write_text(HARNESS.replace('/* PRODUCTION */', production))
subprocess.run([os.environ.get('CC', 'cc'), '-std=c11', '-Wall', '-Wextra',
'-Werror', '-O1', '-fsanitize=address,undefined',
'-I' + str(ROOT / 'compat'), str(c),
str(ROOT / 'compat/exception.c'), '-o', str(exe)], check=True)
subprocess.run([str(exe)], check=True)
if __name__ == '__main__':
main()