/*
 * DF-3909 trigger/analysis — heap OOB writes in
 * tonga_populate_all_graphic_levels() / tonga_populate_all_memory_levels()
 * (sys/dev/drm/amd/powerplay/smumgr/tonga_smumgr.c:709-742, 1107-1133)
 *
 * Kernel path (hardware-gated: Tonga/Fiji + crafted VBIOS):
 *   data->dpm_table.sclk_table.count / mclk_table.count are filled by
 *   smu7_setup_dpm_tables_v1() straight from the VBIOS dependency tables
 *   (u8 entry count, up to 255, deduplicated only).  They are never clamped
 *   to the fixed SMC state arrays:
 *       smc_state_table.GraphicsLevel[SMU72_MAX_LEVELS_GRAPHICS = 8]
 *       smc_state_table.MemoryLevel  [SMU72_MAX_LEVELS_MEMORY   = 4]
 *   tonga_populate_all_graphic_levels() writes GraphicsLevel[i] for
 *   i < sclk_table.count (OOB for i >= 8: stomps MemoryACPILevel,
 *   MemoryLevel[], LinkLevel[], ... and, for large counts, runs past the
 *   kzalloc'd struct tonga_smumgr entirely), and
 *   tonga_populate_all_memory_levels() writes MemoryLevel[i] for
 *   i < mclk_table.count (OOB for i >= 4) plus the unguarded
 *   MemoryLevel[count-1] boot-watermark store.  The clock-stretcher path
 *   additionally READS GraphicsLevel[GraphicsDpmLevelCount-1].
 *
 * Layout facts used below (smu72_discrete.h): GraphicsLevel is followed by
 * MemoryACPILevel (1 x MemoryLevel), then MemoryLevel[4].
 */
#include <stdio.h>

#define GLVLS 8              /* SMU72_MAX_LEVELS_GRAPHICS */
#define MLVLS 4              /* SMU72_MAX_LEVELS_MEMORY   */
#define GLSZ  64             /* sizeof(SMU72_Discrete_GraphicsLevel) ~64B */
#define MLSZ  96             /* sizeof(SMU72_Discrete_MemoryLevel)   ~96B */

int main(void)
{
    uint32_t sclk_count = 16, mclk_count = 9;   /* crafted VBIOS dep tables */
    uint32_t gl_array = GLVLS * GLSZ;           /* GraphicsLevel[8] span    */
    uint32_t i, oob = 0, worst = 0;

    for (i = 0; i < sclk_count; i++) {
        uint32_t off = i * GLSZ;
        if (off + GLSZ > gl_array) { oob++; worst = off + GLSZ; }
    }
    printf("GraphicsLevel[8] span        : %u bytes\n", gl_array);
    printf("sclk dep count (VBIOS u8)    : %u\n", sclk_count);
    printf("OOB GraphicsLevel writes     : %u, worst write ends at GraphicsLevel+%u\n",
           oob, worst);
    printf("  -> stomps MemoryACPILevel/MemoryLevel[4]/LinkLevel[8]/... %s\n",
           worst > gl_array + (MLSZ * 5) + 400 ? "and beyond struct tonga_smumgr"
                                               : "(intra-struct)");

    oob = 0; worst = 0;
    for (i = 0; i < mclk_count; i++) {
        uint32_t off = i * MLSZ;
        if (off + MLSZ > MLVLS * MLSZ) { oob++; worst = off + MLSZ; }
    }
    /* plus the unguarded MemoryLevel[count-1] watermark store */
    uint32_t wm_off = (mclk_count - 1) * MLSZ;
    printf("MemoryLevel[4] span          : %u bytes; mclk count %u\n",
           MLVLS * MLSZ, mclk_count);
    printf("OOB MemoryLevel writes       : %u (worst ends at +%u); watermark store at +%u (also OOB)\n",
           oob, worst, wm_off);
    /* count==0 underflow form: MemoryLevel[0-1] */
    printf("count==0 form                : MemoryLevel[(uint32)(0-1)] = wild write at +4294967295*%u\n",
           MLSZ);
    return 1; /* corruption index math demonstrated */
}
