/*
 * DF-4388 unit test: host-side replica of the central V1 validation layer
 * added to sys/dev/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c
 * (pp_v1_table_range_ok / pp_v1_clamp_count / the VCE + state-entry index
 * treatment).  This exercises the exact clamp/reject arithmetic that the
 * patched kernel parser applies to firmware-supplied values.
 *
 * Build:  cc -O2 -o v1_clamp_test v1_clamp_test.c
 * Run:    ./v1_clamp_test ; echoes PASS/FAIL per case, exit != 0 on FAIL
 */
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

/* ---- replica of the patched helpers (byte-identical logic) ---- */
static uint8_t image[2048];
static uint32_t image_size;

static bool pp_v1_table_range_ok(uint32_t off, uint32_t len)
{
	if (off >= image_size)
		return false;
	return len <= (image_size - off);
}

static uint8_t pp_v1_clamp_count(uint32_t off, uint32_t header_size,
				 uint8_t num, uint32_t entry_size)
{
	uint64_t avail;

	if (!pp_v1_table_range_ok(off, header_size + entry_size))
		return 0;
	avail = ((uint64_t)image_size - off - header_size) / entry_size;
	if (num > avail)
		num = (avail > 255) ? 255 : (uint8_t)avail;
	return num;
}

static int failures;

#define CHECK(name, cond) do { \
	printf("%-58s %s\n", name, (cond) ? "PASS" : "FAIL"); \
	if (!(cond)) failures++; \
} while (0)

int main(void)
{
	uint8_t r;

	/* realistic small PowerPlay image */
	image_size = 512;

	/* 1. DF-1574: lookup count 255 vs dest 16 and image (8 fit) */
	r = pp_v1_clamp_count(16, 2, 255, 8);		/* (512-16-2)/8 = 61 */
	CHECK("DF-1574 count255 clamp->min(61)", r == 61);
	CHECK("DF-1574 dest clamp min(61,16)==16",
	      (uint8_t)(r > 16 ? 16 : r) == 16);

	/* 2. truncated image: only 1 entry fits */
	image_size = 16 + 2 + 8;			/* header+1 entry */
	r = pp_v1_clamp_count(16, 2, 200, 8);
	CHECK("truncated table: count clamps to 1", r == 1);

	/* 3. header itself outside image -> 0 (reject) */
	r = pp_v1_clamp_count(2000, 2, 5, 8);
	CHECK("offset past image -> 0", r == 0);

	/* 4. USHORT offset 0xffff -> range check false */
	CHECK("offset 0xffff rejected", !pp_v1_table_range_ok(0xffff, 4));

	/* 5. DF-1577: entry_index == ucNumEntries must be rejected */
	{
		uint8_t ucNumEntries = 4, entry_index = 4;
		CHECK("DF-1577 entry_index==ucNumEntries rejected",
		      !(entry_index < ucNumEntries));
		entry_index = 3;
		CHECK("DF-1577 entry_index<ucNumEntries accepted",
		      entry_index < ucNumEntries);
	}

	/* 6. VCE indices: UCHAR 255 vs 6 available entries -> fallback 0 */
	{
		uint8_t ucSCLKIndex = 255, sclk_idx;
		image_size = 512;
		r = pp_v1_clamp_count(64, 2, 0xFF, 12);	/* (512-64-2)/12=37 */
		sclk_idx = (ucSCLKIndex >= r) ? 0 : ucSCLKIndex;
		CHECK("VCE ucSCLKIndex 255 clamped to 0", sclk_idx == 0);
	}

	/* 7. new finding: num_vce_state_tables 255 vs AMD_MAX_VCE_LEVELS 6 */
	{
		int i = 255, AMD_MAX_VCE_LEVELS = 6;
		if (i > AMD_MAX_VCE_LEVELS)
			i = AMD_MAX_VCE_LEVELS;
		CHECK("VCE state count 255 clamped to 6", i == 6);
	}

	/* 8. dep-count destination clamps (GraphicsLevel[8]/MemoryLevel[4]) */
	{
		uint8_t sclk = pp_v1_clamp_count(16, 2, 255, 12); /* 41 */
		uint8_t mclk = pp_v1_clamp_count(16, 2, 255, 14); /* 35 */
		if (sclk > 8) sclk = 8;
		if (mclk > 4) mclk = 4;
		CHECK("sclk dep count clamped to 8 (GraphicsLevel[8])", sclk == 8);
		CHECK("mclk dep count clamped to 4 (MemoryLevel[4])", mclk == 4);
	}

	printf(failures ? "\n%d FAILURE(S)\n" : "\nALL CASES PASS\n", failures);
	return failures ? 1 : 0;
}
