/*
 * fxsrc.c — shared harness source-feeder + window plumbing for the
 * DF-4846..DF-4849 feeder harnesses (compiled together with each driver).
 */
#include "fxshim.h"

uint64_t fx_src_ready;
uint32_t fx_src_give;
uint64_t fx_audit_max_off;
uint64_t fx_audit_max_ask;
uint64_t fx_audit_given;
int fx_audit_asks;
int fx_audit_wild;
uintptr_t fx_win_base, fx_win_limit;
uintptr_t fx_arena_base, fx_arena_limit;

__attribute__((weak)) uint32_t
fx_source_feed_impl(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
    uint32_t count, void *source)
{
	uint32_t i, give;

	(void)f; (void)c; (void)source;
	fx_audit_asks++;
	if ((uint64_t)count > fx_audit_max_ask)
		fx_audit_max_ask = count;
	give = (fx_src_give != 0) ? fx_src_give : count;
	if ((uint64_t)give > fx_src_ready)
		give = (uint32_t)fx_src_ready;
	fx_src_ready -= give;		/* a real source dries up */
	if (!fx_in_range(b, give)) {
		/* the feeder asked the source to write outside the guarded
		 * arena (underflow re-arm / wild pointer). Record, skip. */
		fx_audit_wild++;
		return (0);
	}
	for (i = 0; i < give; i++)
		b[i] = (uint8_t)(i & 0x3);	/* tiny amplitude: avoid FEEDMATRIX_CLIP_CHECK errx */
	fx_audit_given += give;
	if ((uintptr_t)b + give > fx_win_base) {
		uint64_t off = ((uintptr_t)b + give) - fx_win_base;
		if (off > fx_audit_max_off)
			fx_audit_max_off = off;
	}
	return (give);
}

int fx_canary_hit;

uint8_t *
fx_win_alloc(uint32_t sz)
{
	unsigned char *p = kmalloc(sz, M_DEVBUF, M_ZERO);
	return ((uint8_t *)p);
}

int
fx_win_check(uint8_t *win, uint32_t sz, const char **where)
{
	unsigned char *base = win - FX_GUARD_FRONT;
	uint32_t i;

	for (i = 0; i < FX_GUARD_FRONT; i++)
		if (base[i] != (unsigned char)0xCB) {
			*where = "FRONT guard (write before window)";
			fx_canary_hit = 1;
			return (-1);
		}
	for (i = 0; i < 128; i++)	/* first 128 rear bytes: any hit is proof */
		if (base[sz + FX_GUARD_FRONT + i] != (unsigned char)0xCA) {
			*where = "REAR guard (write past window)";
			fx_canary_hit = 1;
			return (-1);
		}
	return (0);
}

void
fx_win_free(uint8_t *win)
{
	kfree(win, M_DEVBUF);
}
