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

#define RIG_MAX_CCBS	16

struct rig_queue_args {
	uint32_t	func_code;
	uint32_t	timeout_ms;
	uint32_t	dxfer_len;
	uint32_t	dir_in;
};
struct rig_calcgeom_args {
	uint32_t	block_size, volume_size;
	uint32_t	heads, secs_per_track, cylinders, status;
};
struct rig_comp_args {
	uint32_t	use_recovery;
	uint32_t	ccb_id;
	uint8_t		comp_code, btstat, sdstat, _pad;
};
struct rig_state {
	int32_t		 err;
	uint32_t	 ccb_id;
	uint64_t	 bccb_kva;
	uint64_t	 recovery_kva;
	uint32_t	 bccb_flags;
	uint32_t	 recovery_flags;
	uint32_t	 hccb_opcode, hccb_cmd_len, hccb_sense_len;
	uint32_t	 hccb_datain, hccb_dataout;
	uint32_t	 hccb_btstat, hccb_sdstat;
	uint32_t	 ccb_status;
	uint32_t	 timer_pending;
	uint32_t	 dmamap_nsegs;
	uint32_t	 on_pending_list;
	uint32_t	 comp_count;
	uint32_t	 last_status;
	uint32_t	 async_bdr, async_busreset;
	uint32_t	 vregs[4];
	uint32_t	 outbox_ccbaddr, outbox_action;
	uint32_t	 active_ccbs, free_list_walk, pending_walk;
};
struct rig_vreg_args { uint32_t off, val; };

#define RIGIOC_QUEUE	_IOWR('B', 1, struct rig_queue_args)
#define RIGIOC_SNAP	_IOWR('B', 2, struct rig_state)
#define RIGIOC_COMP	_IOWR('B', 3, struct rig_comp_args)
#define RIGIOC_TIMEOUT	_IOWR('B', 4, struct rig_state)
#define RIGIOC_CALCGEOM	_IOWR('B', 5, struct rig_calcgeom_args)
#define RIGIOC_BTRESET	_IOWR('B', 6, struct rig_state)
#define RIGIOC_VREG	_IOWR('B', 7, struct rig_vreg_args)
#define RIGIOC_HOLD	_IOWR('B', 8, struct rig_state)
#define RIGIOC_REL	_IOWR('B', 9, struct rig_state)
#define RIGIOC_RACE_FIRE _IOWR('B', 10, struct rig_state)
#define RIGIOC_SIMQ_REL	_IOWR('B', 11, struct rig_state)

#define XPT_SCSI_IO_D	0x0901
#define XPT_RESET_DEV_D	0x0912

static void
dump_state(const char *tag, struct rig_state *st)
{
	printf("[%s] err=%d id=%u bccb=0x%llx flags=0x%x "
	    "hccb(op=0x%02x cmd_len=%u sense_len=%u din=%u dout=%u "
	    "btstat=0x%02x sdstat=0x%02x) ccb_status=0x%x timer=%u "
	    "map=%u on_pending=%u comp=%u last_status=0x%x "
	    "rec_kva=0x%llx rec_flags=0x%x async_bdr=%u async_busrst=%u "
	    "vregs=[%02x %02x %02x %02x] outbox(ccb=0x%08x act=0x%02x) "
	    "active=%u free=%u pending=%u\n",
	    tag, st->err, st->ccb_id,
	    (unsigned long long)st->bccb_kva, st->bccb_flags,
	    st->hccb_opcode, st->hccb_cmd_len, st->hccb_sense_len,
	    st->hccb_datain, st->hccb_dataout,
	    st->hccb_btstat, st->hccb_sdstat,
	    st->ccb_status, st->timer_pending, st->dmamap_nsegs,
	    st->on_pending_list, st->comp_count, st->last_status,
	    (unsigned long long)st->recovery_kva, st->recovery_flags,
	    st->async_bdr, st->async_busreset,
	    st->vregs[0], st->vregs[1], st->vregs[2], st->vregs[3],
	    st->outbox_ccbaddr, st->outbox_action,
	    st->active_ccbs, st->free_list_walk, st->pending_walk);
}

static void
prime(const char *kn, const char *sysctl)
{
	char cmd[256], buf[64];
	FILE *p;
	unsigned long v;

	snprintf(cmd, sizeof(cmd), "nm /boot/kernel/kernel | "
	    "awk '$3==\"%s\"{print $1}'", kn);
	p = popen(cmd, "r");
	if (!p || !fgets(buf, sizeof(buf), p)) {
		fprintf(stderr, "prime: symbol %s not found\n", kn);
		exit(1);
	}
	pclose(p);
	v = strtoul(buf, NULL, 16);
	snprintf(cmd, sizeof(cmd), "sysctl -w %s=0x%lx >/dev/null",
	    sysctl, v);
	if (system(cmd) != 0) {
		fprintf(stderr, "prime: sysctl %s failed\n", sysctl);
		exit(1);
	}
	printf("[prime] %s = 0x%lx\n", sysctl, v);
}

int
main(int argc, char **argv)
{
	union {
		struct rig_queue_args qa;
		struct rig_state st;
		struct rig_comp_args ca;
		struct rig_calcgeom_args ga;
		struct rig_vreg_args va;
		char pad[256];
	} u;
	int fd, id;

	if (argc < 2) {
		fprintf(stderr, "usage: %s prime|vreg <off> <val>|queue io|rst "
		    "[timeout_ms]|queuein <ms> <len>|snap <id>|comp <id>|comprec|"
		    "compx <id> <cc> <bt> <sd>|timeout <id>|racefire <id>|"
		    "calcgeom <bs> <vs>|btreset <0|1>|hold|rel|simqrel|wait <sec>\n",
		    argv[0]);
		return (2);
	}
	memset(&u, 0, sizeof(u));
	fd = open("/dev/dfbt0", O_RDWR);
	if (fd < 0) { perror("open /dev/dfbt0"); return (1); }

	if (!strcmp(argv[1], "prime")) {
		prime("btaction", "kern.dfbt_a_btaction");
		prime("btpoll", "kern.dfbt_a_btpoll");
		prime("btdone", "kern.dfbt_a_btdone");
		prime("bttimeout", "kern.dfbt_a_bttimeout");
		prime("btreset", "kern.dfbt_a_btreset");
		prime("bt_intr_locked", "kern.dfbt_a_btintr");
		printf("[prime] all addresses primed\n");
	} else if (!strcmp(argv[1], "vreg") && argc >= 4) {
		u.va.off = atoi(argv[2]);
		u.va.val = strtoul(argv[3], NULL, 0);
		if (ioctl(fd, RIGIOC_VREG, &u) != 0)
			perror("RIGIOC_VREG");
		else
			printf("[vreg] reg[%u]=0x%x\n", u.va.off, u.va.val);
	} else if (!strcmp(argv[1], "queue") && argc >= 3) {
		u.qa.func_code = !strcmp(argv[2], "rst") ?
		    XPT_RESET_DEV_D : XPT_SCSI_IO_D;
		u.qa.timeout_ms = argc >= 4 ? atoi(argv[3]) : 60000;
		u.qa.dxfer_len = 0;
		u.qa.dir_in = 0;
		if (ioctl(fd, RIGIOC_QUEUE, &u) != 0)
			perror("RIGIOC_QUEUE");
		else
			dump_state("queue", &u.st);
	} else if (!strcmp(argv[1], "queuein") && argc >= 4) {
		u.qa.func_code = XPT_SCSI_IO_D;
		u.qa.timeout_ms = atoi(argv[2]);
		u.qa.dxfer_len = atoi(argv[3]);
		u.qa.dir_in = 1;
		if (ioctl(fd, RIGIOC_QUEUE, &u) != 0)
			perror("RIGIOC_QUEUE");
		else
			dump_state("queuein", &u.st);
	} else if (!strcmp(argv[1], "snap") && argc >= 3) {
		u.st.ccb_id = atoi(argv[2]);
		if (ioctl(fd, RIGIOC_SNAP, &u) != 0)
			perror("RIGIOC_SNAP");
		else
			dump_state("snap", &u.st);
	} else if (!strcmp(argv[1], "comp") && argc >= 3) {
		/* clean error-free completion: BMBI_ERROR+NOERROR/OK */
		u.ca.use_recovery = 0;
		u.ca.ccb_id = atoi(argv[2]);
		u.ca.comp_code = 0x04;	/* BMBI_ERROR */
		u.ca.btstat = 0x00;	/* BTSTAT_NOERROR */
		u.ca.sdstat = 0x00;	/* SCSI_STATUS_OK */
		if (ioctl(fd, RIGIOC_COMP, &u) != 0)
			perror("RIGIOC_COMP");
		else
			dump_state("comp", &u.st);
	} else if (!strcmp(argv[1], "comprec")) {
		u.ca.use_recovery = 1;
		u.ca.comp_code = 0x01;	/* BMBI_OK */
		if (ioctl(fd, RIGIOC_COMP, &u) != 0)
			perror("RIGIOC_COMP");
		else
			dump_state("comprec", &u.st);
	} else if (!strcmp(argv[1], "compx") && argc >= 6) {
		u.ca.use_recovery = 0;
		u.ca.ccb_id = atoi(argv[2]);
		u.ca.comp_code = strtoul(argv[3], NULL, 0);
		u.ca.btstat = strtoul(argv[4], NULL, 0);
		u.ca.sdstat = strtoul(argv[5], NULL, 0);
		if (ioctl(fd, RIGIOC_COMP, &u) != 0)
			perror("RIGIOC_COMP");
		else
			dump_state("compx", &u.st);
	} else if (!strcmp(argv[1], "timeout") && argc >= 3) {
		u.st.ccb_id = atoi(argv[2]);
		if (ioctl(fd, RIGIOC_TIMEOUT, &u) != 0)
			perror("RIGIOC_TIMEOUT");
		else
			dump_state("timeout", &u.st);
	} else if (!strcmp(argv[1], "racefire") && argc >= 3) {
		u.st.ccb_id = atoi(argv[2]);
		if (ioctl(fd, RIGIOC_RACE_FIRE, &u) != 0)
			perror("RIGIOC_RACE_FIRE");
		else
			printf("[racefire] stale bttimeout thread entered\n");
	} else if (!strcmp(argv[1], "calcgeom") && argc >= 4) {
		u.ga.block_size = strtoul(argv[2], NULL, 0);
		u.ga.volume_size = strtoul(argv[3], NULL, 0);
		if (ioctl(fd, RIGIOC_CALCGEOM, &u) != 0)
			perror("RIGIOC_CALCGEOM");
		else
			printf("[calcgeom] survived: heads=%u spt=%u cyl=%u "
			    "status=0x%x\n", u.ga.heads, u.ga.secs_per_track,
			    u.ga.cylinders, u.ga.status);
	} else if (!strcmp(argv[1], "btreset") && argc >= 3) {
		u.st.hccb_opcode = atoi(argv[2]);
		if (ioctl(fd, RIGIOC_BTRESET, &u) != 0)
			perror("RIGIOC_BTRESET");
		else
			dump_state("btreset", &u.st);
	} else if (!strcmp(argv[1], "hold")) {
		if (ioctl(fd, RIGIOC_HOLD, &u) != 0)
			perror("RIGIOC_HOLD");
	} else if (!strcmp(argv[1], "rel")) {
		if (ioctl(fd, RIGIOC_REL, &u) != 0)
			perror("RIGIOC_REL");
	} else if (!strcmp(argv[1], "simqrel")) {
		if (ioctl(fd, RIGIOC_SIMQ_REL, &u) != 0)
			perror("RIGIOC_SIMQ_REL");
	} else if (!strcmp(argv[1], "wait") && argc >= 3) {
		id = atoi(argv[2]);
		printf("[wait] sleeping %ds...\n", id);
		sleep(id);
	} else {
		fprintf(stderr, "unknown cmd\n");
		close(fd);
		return (2);
	}
	close(fd);
	return (0);
}