/*
 * DF-4665 PATCHED twin — same harness as smu7_odn_overflow.c but the copy
 * helper carries the fix.diff guard (max_entries parameter + clamp, and the
 * smu7 call sites pass MAX_REGULAR_DPM_NUMBER). Expected: entries[8..]
 * untouched, canaries intact, dep_table->count == 8.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

struct phm_ppt_v1_clock_voltage_dependency_record {
	uint32_t clk;
	uint8_t  vddInd;
	uint8_t  vddciInd;
	uint8_t  mvddInd;
	uint16_t vdd_offset;
	uint16_t vddc;
	uint16_t vddgfx;
	uint16_t vddci;
	uint16_t mvdd;
	uint8_t  phases;
	uint8_t  cks_enable;
	uint8_t  cks_voffset;
	uint32_t sclk_offset;
};

struct phm_ppt_v1_clock_voltage_dependency_table {
	uint32_t count;
	struct phm_ppt_v1_clock_voltage_dependency_record entries[1];
};

#define MAX_REGULAR_DPM_NUMBER 8

struct phm_odn_performance_level {
	uint32_t clock;
	uint32_t vddc;
	int      enabled;
};

struct phm_odn_clock_levels {
	uint32_t size;
	uint32_t options;
	uint32_t flags;
	uint32_t num_of_pl;
	struct phm_odn_performance_level entries[8];
};

struct smu7_odn_clock_voltage_dependency_table {
	uint32_t count;
	struct phm_ppt_v1_clock_voltage_dependency_record entries[MAX_REGULAR_DPM_NUMBER];
};

struct smu7_odn_dpm_table {
	struct phm_odn_clock_levels odn_core_clock_dpm_levels;
	struct phm_odn_clock_levels odn_memory_clock_dpm_levels;
	struct smu7_odn_clock_voltage_dependency_table vdd_dependency_on_sclk;
	struct smu7_odn_clock_voltage_dependency_table vdd_dependency_on_mclk;
	uint32_t odn_mclk_min_limit;
	uint32_t min_vddc;
	uint32_t max_vddc;
};

struct smu7_backend_like {
	char dummy_dpm_tables[1024];
	struct smu7_odn_dpm_table odn_dpm_table;
	uint32_t canary[64];
};

#define CANARY 0xDEADBEEFU
#define EINVAL 22

/* PATCHED per fix.diff: destination capacity is passed and clamped */
int smu_get_voltage_dependency_table_ppt_v1_fixed(
		const struct phm_ppt_v1_clock_voltage_dependency_table *allowed_dep_table,
		struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
		uint32_t max_entries)
{
	uint32_t i;

	if (!allowed_dep_table || allowed_dep_table->count == 0)
		return -EINVAL;

	dep_table->count = (allowed_dep_table->count > max_entries)
			? max_entries : allowed_dep_table->count;
	for (i = 0; i < dep_table->count; i++) {
		dep_table->entries[i].clk = allowed_dep_table->entries[i].clk;
		dep_table->entries[i].vddInd = allowed_dep_table->entries[i].vddInd;
		dep_table->entries[i].vdd_offset = allowed_dep_table->entries[i].vdd_offset;
		dep_table->entries[i].vddc = allowed_dep_table->entries[i].vddc;
		dep_table->entries[i].vddgfx = allowed_dep_table->entries[i].vddgfx;
		dep_table->entries[i].vddci = allowed_dep_table->entries[i].vddci;
		dep_table->entries[i].mvdd = allowed_dep_table->entries[i].mvdd;
		dep_table->entries[i].phases = allowed_dep_table->entries[i].phases;
		dep_table->entries[i].cks_enable = allowed_dep_table->entries[i].cks_enable;
		dep_table->entries[i].cks_voffset = allowed_dep_table->entries[i].cks_voffset;
	}
	return 0;
}

static struct phm_ppt_v1_clock_voltage_dependency_table *make_dep_table(unsigned count,
		unsigned distinct_clocks)
{
	struct phm_ppt_v1_clock_voltage_dependency_table *t;
	unsigned i;

	t = calloc(1, sizeof(*t) + sizeof(struct phm_ppt_v1_clock_voltage_dependency_record) * (count + 1));
	if (!t) { perror("calloc"); exit(1); }
	t->count = count;
	for (i = 0; i < count; i++) {
		t->entries[i].clk = 100000 + (i % distinct_clocks) * 2500;
		t->entries[i].vddInd = (uint8_t)i;
		t->entries[i].vdd_offset = (uint16_t)(0x1100 + i);
		t->entries[i].vddc = (uint16_t)(900 + i);
		t->entries[i].vddgfx = (uint16_t)(950 + i);
		t->entries[i].vddci = (uint16_t)(1000 + i);
		t->entries[i].mvdd = (uint16_t)(1050 + i);
		t->entries[i].phases = 1;
		t->entries[i].cks_enable = (uint8_t)(i & 1);
		t->entries[i].sclk_offset = 0x41410000u + i;
	}
	return t;
}

static unsigned count_smashed(const uint32_t *c, unsigned n)
{
	unsigned i, s = 0;
	for (i = 0; i < n; i++)
		if (c[i] != CANARY)
			s++;
	return s;
}

int main(int argc, char **argv)
{
	unsigned count = (argc > 1) ? (unsigned)atoi(argv[1]) : 16;
	unsigned distinct = (argc > 2) ? (unsigned)atoi(argv[2]) : 2;
	struct smu7_backend_like *be = calloc(1, sizeof(*be));
	struct phm_ppt_v1_clock_voltage_dependency_table *dep = make_dep_table(count, distinct);

	for (size_t k = 0; k < 64; k++)
		be->canary[k] = CANARY;

	smu_get_voltage_dependency_table_ppt_v1_fixed(dep,
		(struct phm_ppt_v1_clock_voltage_dependency_table *)
		&be->odn_dpm_table.vdd_dependency_on_sclk,
		MAX_REGULAR_DPM_NUMBER);	/* the fix: smu7 call sites pass 8 */

	printf("[patched] dep->count=%u -> stored count = %u (clamped to MAX_REGULAR_DPM_NUMBER)\n",
	       count, be->odn_dpm_table.vdd_dependency_on_sclk.count);
	printf("[patched] vdd_dependency_on_mclk.count still = %u\n",
	       be->odn_dpm_table.vdd_dependency_on_mclk.count);
	printf("[patched] canary words smashed: %u/64\n", count_smashed(be->canary, 64));
	if (be->odn_dpm_table.vdd_dependency_on_sclk.count <= MAX_REGULAR_DPM_NUMBER &&
	    count_smashed(be->canary, 64) == 0 &&
	    be->odn_dpm_table.vdd_dependency_on_mclk.count == 0)
		printf("FIX VALIDATED: no out-of-bounds writes with clamped copy\n");
	else {
		printf("FIX FAILED\n");
		return 1;
	}
	return 0;
}
