/* DF-3621 / DF-3622 combined arithmetic proof: the two divide-by-zero sinks
 * fed by VBIOS-controlled zero spread-spectrum rate and zero MPLL reference
 * clock in amdgpu ci_dpm.c.
 *
 * Sink A (DF-3621) — ci_calculate_sclk_params, ci_dpm.c:3337:
 *     clk_s = reference_clock * 5 / (reference_divider * ss.rate);
 * Sink B (DF-3621) — ci_calculate_mclk_params, ci_dpm.c:2981:
 *     clks  = reference_clock * 5 / ss.rate;
 *   ss.rate comes from amdgpu_atombios_get_asic_ss_info (amdgpu_atombios.c:896):
 *     v1: ss->rate = usSpreadRateInKhz            (:936) — NO zero check
 *     v2: ss->rate = usSpreadRateIn10Hz / 100      (:954,:959) — truncates to 0 for <100
 *     v3: ss->rate = usSpreadRateIn10Hz / 100      (:976,:984) — truncates to 0 for <100
 *
 * Sink C (DF-3622) — ci_calculate_mclk_params, ci_dpm.c:2977:
 *     tmp = (freq_nom / reference_clock);
 *   reference_clock = adev->clock.mpll.reference_freq, assigned unchecked from
 *   FirmwareInfo.usMemoryReferenceClock (amdgpu_atombios.c:659-660).
 *
 * Kernel integer division by zero on x86-64 => #DE => panic.
 *
 * Build: cc -O2 -Wall -o ci_ss_div0 ci_ss_div0.c && ./ci_ss_div0
 * The divisions below are the real machine divisions with volatile sinks so
 * they cannot be optimized away; the SIGFPE trap is the userspace equivalent
 * of the kernel #DE panic.
 */
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <stdlib.h>

volatile uint32_t vsink;

static void sigfpe_handler(int sig) {
	(void)sig;
	printf("    -> divide-by-zero trapped (%s in userspace == #DE in kernel) -> PANIC\n",
	       sig == SIGFPE ? "SIGFPE" : "SIGILL");
	exit(0);
}

int main(void)
{
	/* volatile: models a value freshly read from the VBIOS image; the
	 * compiler cannot const-fold the divisors away */
	volatile uint16_t usSpreadRateIn10Hz = 50;   /* VBIOS value < 100 */
	volatile uint32_t usSpreadRateInKhz   = 0;   /* VBIOS v1 field == 0 */
	volatile uint32_t usMemoryReferenceClock = 0;/* FirmwareInfo field == 0 */
	uint32_t v2_rate = usSpreadRateIn10Hz;       /* amdgpu_atombios.c:954 */
	v2_rate /= 100;                              /* :959  -> 0 */
	printf("V2/V3 parser: usSpreadRateIn10Hz=%u -> ss.rate after /=100 = %u\n\n",
	       usSpreadRateIn10Hz, v2_rate);

	signal(SIGFPE, sigfpe_handler);
	signal(SIGILL, sigfpe_handler);

	printf("[A] ci_calculate_sclk_params ci_dpm.c:3337 with ss.rate=0 (v1 zero or v2/v3 truncation)\n");
	printf("    clk_s = reference_clock * 5 / (reference_divider * ss.rate)\n");
	fflush(stdout);
	vsink = 100000 * 5 / (2 * v2_rate);     /* ci_dpm.c:3337 — rate==0 */

	printf("[B] ci_calculate_mclk_params ci_dpm.c:2981 with ss.rate=0\n");
	printf("    clks = reference_clock * 5 / ss.rate\n");
	fflush(stdout);
	vsink = 100000 * 5 / usSpreadRateInKhz; /* ci_dpm.c:2981 — v1 usSpreadRateInKhz==0 */

	printf("[C] ci_calculate_mclk_params ci_dpm.c:2977 with mpll.reference_freq=0\n");
	printf("    (usMemoryReferenceClock=0 from FirmwareInfo, amdgpu_atombios.c:659-660)\n");
	printf("    tmp = freq_nom / reference_clock\n");
	fflush(stdout);
	vsink = 600000 / usMemoryReferenceClock;/* ci_dpm.c:2977 — reference_clock==0 */

	printf("no trap (unexpected)\n");
	return 0;
}
