⬢ DragonFlyBSD Kernel Audit
DF-4695 / trigger_sili_pm.c
← back to finding ↓ download raw
/*
 * trigger_sili_pm.c - userland driver for the sili_pm.c defect family
 * (DF-4692 / DF-4693 / DF-4694 / DF-4695)
 *
 * On real hardware (SiI 3124/3132 HBA + SiL3726 port multiplier) this drives
 * the exact CAM entry points that reach the defective sili_pm.c code:
 *
 *   - camcontrol reset  <t>  -> XPT_RESET_DEV -> sili_cam.c:904-915 ->
 *                              sili_port_reset(ap, atx, 0) ->
 *                              sili_pm_softreset()          (DF-4694, DF-4695)
 *   - camcontrol rescan <b>  -> wildcard XPT_PATH_INQ -> port state machine ->
 *                              sili.c:530 sili_pm_read(EINFO),
 *                              sili.c:580 sili_pm_check_good ->
 *                              sili_pm_read/sili_pm_write     (DF-4692, DF-4693)
 *
 * while the parallel dd loop keeps the port's ccb pool saturated and other
 * PM-target commands on-chip (the co-activity precondition).
 *
 * On the audit QEMU guest there is no sili(4) HBA at all (PCI inventory:
 * PIIX3 IDE + virtio only), so this trigger runs to completion against the
 * ata0/ata1 buses and documents the hardware gate honestly.
 *
 * cc -O2 -Wall -o trigger_sili_pm trigger_sili_pm.c
 * ./trigger_sili_pm <seconds>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>

static void
run(const char *fmt, ...)
{
	char cmd[256];
	va_list ap;

	va_start(ap, fmt);
	vsnprintf(cmd, sizeof(cmd), fmt, ap);
	va_end(ap);
	printf("+ %s\n", cmd);
	fflush(stdout);
	if (system(cmd) != 0)
		printf("  (nonzero exit)\n");
}

int
main(int ac, char **av)
{
	time_t deadline;
	char buses[8][32];
	int nbusses = 0, i;
	FILE *p;
	char line[256];

	if (ac > 1)
		deadline = time(NULL) + atoi(av[1]);
	else
		deadline = time(NULL) + 15;

	printf("== sili_pm defect family trigger ==\n");
	printf("CAM device list:\n");
	if ((p = popen("camcontrol devlist 2>&1", "r")) != NULL) {
		while (fgets(line, sizeof(line), p)) {
			printf("  %s", line);
			if (nbusses < 8 && sscanf(line, "on scbus%*d") == 0) {
				char *b = strstr(line, "bus");
				if (b)
					;
			}
		}
		pclose(p);
	}
	/* enumerate scbus numbers for rescan */
	if ((p = popen("camcontrol devlist -v 2>/dev/null | "
		       "grep '^scbus' | awk '{print $1}'", "r")) != NULL) {
		while (nbusses < 8 && fgets(line, sizeof(line), p)) {
			line[strcspn(line, "\r\n")] = 0;
			snprintf(buses[nbusses], sizeof(buses[0]), "%s",
				 line + 5 /* skip "scbus" */);
			if (buses[nbusses][0])
				nbusses++;
		}
		pclose(p);
	}
	printf("enumerated %d cam bus(es)\n", nbusses);

	/*
	 * Saturation driver: keep a pending I/O stream on every direct disk
	 * (on PM hardware this would be dd through the PM targets).
	 */
	if (system("for d in /dev/da*; do "
		   "(dd if=$d of=/dev/null bs=64k count=400 "
		   ">/dev/null 2>&1 &) ; done") != 0)
		;

	while (time(NULL) < deadline) {
		for (i = 0; i < nbusses; i++) {
			run("camcontrol rescan %s", buses[i]);
			/* XPT_RESET_DEV on every present target */
			run("camcontrol reset %s 2>&1", buses[i]);
		}
		sleep(1);
	}
	printf("== trigger loop finished ==\n");
	return (0);
}