/*-
 * vpd_shim.h -- userspace shim that lets the VERBATIM-extracted VPD parser
 * from sys/bus/pci/pci.c compile and run in a user process.
 *
 * The kernel API surface (kmalloc/kprintf/KASSERT/DELAY/le32toh, the
 * PCIB_READ_CONFIG/PCIB_WRITE_CONFIG dispatch, pcicfgregs) is mapped onto:
 *   - a guard-page allocator (every allocation is followed by a PROT_NONE
 *     page, so the first out-of-bounds byte write past a chunk faults at a
 *     precisely known offset), with allocation accounting;
 *   - a simulated malicious PCI device that serves an attacker-controlled
 *     VPD byte stream and completes every VPD data transfer immediately
 *     (bit 15 of the VPD address register), which is exactly the behaviour
 *     of an emulated/compromised device.
 *
 * DF-5276 / DF-5277 / DF-5278 evidence harness.  Not part of the kernel.
 */
#ifndef VPD_SHIM_H
#define VPD_SHIM_H

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

/* ---------------- kernel API surface ---------------- */

struct shim_dev { int unused; };
typedef struct shim_dev *device_t;
struct shim_malloc_type;
#define M_DEVBUF	((struct shim_malloc_type *)0)
#define M_WAITOK	0
#define M_NOWAIT	1
#define M_ZERO		2

#ifndef KASSERT
#define KASSERT(x, y)	((void)0)
#endif
#define DELAY(us)	do { } while (0)	/* malicious device: no delay */

#define kprintf		printf

/* pcicfgregs is mapped by vpd_harness.c AFTER the verbatim vpd structs
 * (vpd_structs.h) have been included; same for pci_devinfo. */

/* ---------------- guard-page allocator + accounting ---------------- */

struct sh_blk {
	void		*base;	/* mmap base */
	void		*p;	/* returned pointer (== base) */
	size_t		sz;	/* requested size */
	size_t		mapsz;	/* total mapped (incl. guard page) */
	struct sh_blk	*next;
};

extern size_t	sh_nalloc, sh_nrealloc, sh_bytes, sh_maxblk;
extern const char *sh_phase;	/* current test phase, for the SEGV report */

void *sh_kmalloc(size_t sz, int flags);
void *sh_krealloc(void *p, size_t sz);
void  sh_kfree(void *p);
struct sh_blk *sh_find_block(const void *addr);

#define kmalloc(sz, type, flags)	sh_kmalloc((sz), (flags))
#define krealloc(p, sz, type, flags)	sh_krealloc((p), (sz))
#define kfree(p, type)			sh_kfree((p))

/* ---------------- simulated malicious PCI device ---------------- */

#define SIM_VPDBASE	0x30	/* offset of the forged VPD capability */
#define SIM_PCIR_VPD_ADDR	0x2
#define SIM_PCIR_VPD_DATA	0x4

/* verbatim sys/bus/pci/pcireg.h:527-528 */
#define PCIR_VPD_ADDR		0x2
#define PCIR_VPD_DATA		0x4

extern uint8_t	sim_stream[1 << 20];
extern size_t	sim_len;		/* crafted prefix length */
extern size_t	sim_repeat_off, sim_repeat_len;
extern int	sim_endless;		/* wrap the repeat region forever */
extern size_t	sim_serve_cap;		/* stop completing past this offset */
extern size_t	sim_reads, sim_addr_writes;

void	sim_reset(void);
uint32_t sim_read_config(int reg, int width);
void	sim_write_config(int reg, uint32_t val, int width);

#define PCIB_READ_CONFIG(pcib, b, s, f, n, w)  sim_read_config(n, w)
#define PCIB_WRITE_CONFIG(pcib, b, s, f, n, v, w) sim_write_config(n, v, w)

/* These two macros are live in pci.c from pci_read_capabilities() down
 * through pci_read_vpd() ("REG and WREG use carry through"), so the shim
 * defines them exactly as sys/bus/pci/pci.c:850-851 does. */
#define REG(n, w)	PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
#define WREG(n, v, w)	PCIB_WRITE_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, v, w)

#endif /* VPD_SHIM_H */
