⬢ DragonFlyBSD Kernel Audit
DF-4240 / dfdpt_ctl.c
← back to finding ↓ download raw
/*
 * dfdpt_ctl.c -- userland driver for the dfdpt rig (/dev/dfdpt0).
 * Build: cc -O2 -o dfdpt_ctl dfdpt_ctl.c
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>

struct rig_state {
	int32_t		 err;
	uint32_t	 ccb_id;
	uint64_t	 ccb_kva;
	uint64_t	 free_head;
	uint64_t	 free_second;
	uint32_t	 free_selfloop;
	uint32_t	 free_dups;
	uint32_t	 free_walk;
	uint32_t	 free_softc_ctr;
	uint32_t	 pending_walk;
	uint32_t	 ccb_status;
	uint32_t	 comp_count;
	uint32_t	 timer_pending;
	uint32_t	 dccb_state;
	uint32_t	 sp_busaddr;
	uint32_t	 sp_eoc;
	/* ioctl inputs */
	uint32_t	 in_timeout;
	uint32_t	 in_eoc, in_hba, in_scsi, in_serr, in_dpth;
	uint32_t	 in_channels;
};
struct rig_chan8_args {
	uint32_t	 channels;
	uint32_t	 attach_ret;
	uint32_t	 channels_after;
	uint32_t	 canary_cp_before, canary_cp_after;
	uint32_t	 canary_li_before, canary_li_after;
	uint64_t	 oob_slot[10];
	uint32_t	 runaway;
	uint32_t	 path3_is_sim;
	uint64_t	 member_off[10];
};
struct rig_panic_args {
	uint32_t	 ccb_id;
};

#define RIGIOC_QUEUE	_IOWR('D', 1, struct rig_state)
#define RIGIOC_INTR	_IOWR('D', 2, struct rig_state)
#define RIGIOC_SNAP	_IOWR('D', 3, struct rig_state)
#define RIGIOC_ALIAS	_IOWR('D', 4, struct rig_state)
#define RIGIOC_CHAN8	_IOWR('D', 5, struct rig_chan8_args)
#define RIGIOC_PANIC	_IOWR('D', 6, struct rig_panic_args)
#define RIGIOC_DUPALIAS	_IOWR('D', 7, struct rig_state)
#define RIGIOC_FULLINIT	_IOWR('D', 8, struct rig_chan8_args)

static int fd = -1;

static void
dump_state(const char *tag, struct rig_state *st)
{
	printf("[%s] id=%u dccb=0x%llx state=%u ccb_status=0x%x "
	    "comp=%u timer=%u pending=%u | free: head=0x%llx "
	    "second=0x%llx selfloop=%u dups=%u walk=%u ctr=%u | "
	    "sp(bus=0x%x eoc=%u)\n",
	    tag, st->ccb_id, (unsigned long long)st->ccb_kva,
	    st->dccb_state, st->ccb_status, st->comp_count,
	    st->timer_pending, st->pending_walk,
	    (unsigned long long)st->free_head,
	    (unsigned long long)st->free_second, st->free_selfloop,
	    st->free_dups, st->free_walk, st->free_softc_ctr,
	    st->sp_busaddr, st->sp_eoc);
}

static int
prime(const char *kn, const char *sysctl)
{
	char buf[256];
	FILE *f;
	unsigned long v;

	snprintf(buf, sizeof(buf),
	    "/usr/bin/nm /boot/kernel/kernel | grep ' %s$'", kn);
	f = popen(buf, "r");
	if (f == NULL || fgets(buf, sizeof(buf), f) == NULL) {
		fprintf(stderr, "prime: symbol %s not found\n", kn);
		if (f)
			pclose(f);
		return (-1);
	}
	pclose(f);
	v = strtoul(buf, NULL, 16);
	if (v == 0) {
		fprintf(stderr, "prime: bad addr for %s: %s\n", kn, buf);
		return (-1);
	}
	if (sysctlbyname(sysctl, NULL, 0, &v, sizeof(v)) != 0) {
		fprintf(stderr, "prime: sysctl %s failed\n", sysctl);
		return (-1);
	}
	printf("[prime] %s = 0x%lx\n", sysctl, v);
	return (0);
}

static void
mustopen(void)
{
	if (fd >= 0)
		return;
	fd = open("/dev/dfdpt0", O_RDWR);
	if (fd < 0)
		err(1, "open /dev/dfdpt0");
}

int
main(int argc, char **argv)
{
	struct rig_state st;

	if (argc < 2) {
		fprintf(stderr, "usage: %s prime | queue [tmo_ms] | "
		    "intr <id> <eoc> <hba> <scsi> [serr] [dpth] | snap <id> | "
		    "alias | chan8 <ch> | panic [id] | magic\n", argv[0]);
		return (1);
	}
	mustopen();
	memset(&st, 0, sizeof(st));

	if (!strcmp(argv[1], "prime")) {
		int rc = 0;
		rc |= prime("dpt_action", "kern.dfdpt_a_dptaction");
		rc |= prime("dpt_poll", "kern.dfdpt_a_dptpoll");
		rc |= prime("dptallocccbs", "kern.dfdpt_a_dptallocccbs");
		return (rc == 0 ? 0 : 1);
	}
	if (!strcmp(argv[1], "magic")) {
		unsigned long v;
		size_t l;
		char *names[] = { "kern.dfdpt_m_dptaction",
		    "kern.dfdpt_m_dptpoll", "kern.dfdpt_m_dptallocccbs" };
		for (int i = 0; i < 3; i++) {
			l = sizeof(v);
			if (sysctlbyname(names[i], &v, &l, NULL, 0) != 0)
				err(1, names[i]);
			printf("[magic] %s = 0x%lx\n", names[i], v);
		}
		return (0);
	}
	if (!strcmp(argv[1], "queue")) {
		st.in_timeout = argc > 2 ? strtoul(argv[2], NULL, 0) : 60000;
		if (ioctl(fd, RIGIOC_QUEUE, &st) != 0)
			err(1, "QUEUE");
		dump_state("queue", &st);
		return (0);
	}
	if (!strcmp(argv[1], "snap")) {
		st.ccb_id = argc > 2 ? strtoul(argv[2], NULL, 0) : 0;
		if (ioctl(fd, RIGIOC_SNAP, &st) != 0)
			err(1, "SNAP");
		dump_state("snap", &st);
		return (0);
	}
	if (!strcmp(argv[1], "intr")) {
		st.ccb_id = argc > 2 ? strtoul(argv[2], NULL, 0) : 0;
		st.in_eoc = argc > 3 ? strtoul(argv[3], NULL, 0) : 1;
		st.in_hba = argc > 4 ? strtoul(argv[4], NULL, 0) : 0;
		st.in_scsi = argc > 5 ? strtoul(argv[5], NULL, 0) : 0;
		st.in_serr = argc > 6 ? strtoul(argv[6], NULL, 0) : 0;
		st.in_dpth = argc > 7 ? strtoul(argv[7], NULL, 0) : 0;
		if (ioctl(fd, RIGIOC_INTR, &st) != 0)
			err(1, "INTR");
		dump_state("intr", &st);
		return (0);
	}
	if (!strcmp(argv[1], "dupalias")) {
		st.ccb_id = argc > 2 ? strtoul(argv[2], NULL, 0) : 0;
		if (ioctl(fd, RIGIOC_DUPALIAS, &st) != 0)
			err(1, "DUPALIAS");
		printf("[dupalias] free_head=0x%llx second=0x%llx "
		    "selfloop=%u dups=%u walk=%u ctr=%u\n",
		    (unsigned long long)st.free_head,
		    (unsigned long long)st.free_second, st.free_selfloop,
		    st.free_dups, st.free_walk, st.free_softc_ctr);
		printf("[dupalias] queue#A dccb=0x%llx queue#B dccb=0x%llx "
		    "SHARED=%u\n",
		    (unsigned long long)st.ccb_kva,
		    (unsigned long long)st.free_head, st.free_selfloop);
		return (0);
	}
	if (!strcmp(argv[1], "alias")) {
		if (ioctl(fd, RIGIOC_ALIAS, &st) != 0)
			err(1, "ALIAS");
		printf("[alias] queue#A dccb=0x%llx queue#B dccb=0x%llx "
		    "SHARED=%u statusA=0x%x statusB=0x%x\n",
		    (unsigned long long)st.ccb_kva,
		    (unsigned long long)st.free_head, st.free_selfloop,
		    st.ccb_status, st.comp_count);
		return (0);
	}
	if (!strcmp(argv[1], "fullinit")) {
		struct rig_chan8_args ca;
		memset(&ca, 0, sizeof(ca));
		ca.channels = argc > 2 ? strtoul(argv[2], NULL, 0) : 8;
		if (ioctl(fd, RIGIOC_FULLINIT, &ca) != 0)
			err(1, "FULLINIT");
		printf("[fullinit] maxchan+1=%u attach_ret=%u "
		    "channels_after=%u canary_cp 0x%x->0x%x canary_li "
		    "0x%x->0x%x path0_holds_sim=%u runaway=%u\n",
		    ca.channels, ca.attach_ret, ca.channels_after,
		    ca.canary_cp_before, ca.canary_cp_after,
		    ca.canary_li_before, ca.canary_li_after,
		    ca.path3_is_sim, ca.runaway);
		for (int i = 0; i < 10; i++)
			printf("[fullinit] slot[%d] off=%llu val=0x%llx\n",
			    i, (unsigned long long)ca.member_off[i],
			    (unsigned long long)ca.oob_slot[i]);
		return (0);
	}
	if (!strcmp(argv[1], "chan8")) {
		struct rig_chan8_args ca;
		memset(&ca, 0, sizeof(ca));
		ca.channels = argc > 2 ? strtoul(argv[2], NULL, 0) : 8;
		if (ioctl(fd, RIGIOC_CHAN8, &ca) != 0)
			err(1, "CHAN8");
		printf("[chan8] channels=%u attach_ret=%u "
		    "channels_after=%u canary_cp 0x%x->0x%x canary_li "
		    "0x%x->0x%x path0_holds_sim=%u runaway=%u\n",
		    ca.channels, ca.attach_ret, ca.channels_after,
		    ca.canary_cp_before, ca.canary_cp_after,
		    ca.canary_li_before, ca.canary_li_after,
		    ca.path3_is_sim, ca.runaway);
		for (int i = 0; i < 10; i++)
			printf("[chan8] slot[%d] off=%llu val=0x%llx\n",
			    i, (unsigned long long)ca.member_off[i],
			    (unsigned long long)ca.oob_slot[i]);
		return (0);
	}
	if (!strcmp(argv[1], "panic")) {
		struct rig_panic_args pa;
		memset(&pa, 0, sizeof(pa));
		pa.ccb_id = argc > 2 ? strtoul(argv[2], NULL, 0) : 0;
		if (ioctl(fd, RIGIOC_PANIC, &pa) != 0)
			err(1, "PANIC");
		printf("[panic] dpt_intr returned unexpectedly!\n");
		return (0);
	}
	fprintf(stderr, "unknown cmd %s\n", argv[1]);
	return (1);
}