⬢ DragonFlyBSD Kernel Audit
DF-2303 / fwpoc.c
← back to finding ↓ download raw
/*
 * fwpoc.c -- userland trigger for the fwohci.c verification rig.
 *
 * opuser (operator group) commands run against /dev/fw0.0 through the
 * REAL firewire_ops -> fw_ioctl -> fc->ioctl (fwohci_ioctl) chain:
 *
 *   fwpoc wr <hexaddr> <hexdata>     FWOHCI_WRREG   (DF-2300)
 *   fwpoc rd <hexaddr>               FWOHCI_RDREG   (DF-2300)
 *   fwpoc phyw <addr> <data>         FWOHCI_WRPHYREG(DF-2300)
 *   fwpoc phyr <addr>                FWOHCI_RDPHYREG(DF-2300)
 *   fwpoc dumpdma <ch>               DUMPDMA        (DF-2301)
 *
 * root-only rig commands run against /dev/fwoctl0:
 *   fwpoc ctl init|dbinit|descset|regwr|regrd|phyrd|rxen|arcv|memeat|
 *            kread|state ...
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

#include "fwohci_harness.h"

/* firewire.h ioctl ABI (must match the stock kernel header) */
struct fw_reg_req {
	unsigned long addr;
	unsigned long data;
};
#define FW_FWOHCI_RDREG	_IOWR('S', 80, struct fw_reg_req)
#define FW_FWOHCI_WRREG	_IOWR('S', 81, struct fw_reg_req)
#define FW_FWOHCI_RDPHYREG _IOWR('S', 82, struct fw_reg_req)
#define FW_FWOHCI_WRPHYREG _IOWR('S', 83, struct fw_reg_req)
#define FW_DUMPDMA	_IOWR('S', 82, u_int32_t)	/* size 4: distinct
							 * from RDPHYREG */

static int
fw_open_dev(void)
{
	int fd = open("/dev/fw0.0", O_RDWR);

	if (fd < 0) {
		perror("open /dev/fw0.0");
		exit(1);
	}
	return (fd);
}

static int
ctl_open_dev(void)
{
	int fd = open("/dev/fwoctl0", O_RDWR);

	if (fd < 0) {
		perror("open /dev/fwoctl0");
		exit(1);
	}
	return (fd);
}

int
main(int argc, char **argv)
{
	int fd, rc;
	struct fw_reg_req r;
	u_int32_t dmach;
	struct fwo_initreq ir;
	struct fwo_dbinit db;
	struct fwo_descset ds;
	struct fwo_arcvreq ar;
	struct fwo_memeat mm;
	struct fwo_kread kr;
	struct fwo_statereq st;
	struct fwo_regreq rr;

	if (argc < 2) {
usage:
		fprintf(stderr,
		    "usage: %s wr addr data | rd addr | phyw a d | "
		    "phyr a | dumpdma ch\n"
		    "       %s ctl init | dbinit ch ndesc ndb psize parent "
		    "elem | descset elem canary | regwr a d | regrd a | "
		    "phyrd\n"
		    "       %s ctl rxen ch | arcv mode F tcode inj | "
		    "memeat free ch | kread hexaddr | state ch\n",
		    argv[0], argv[0], argv[0]);
		exit(2);
	}

	if (!strcmp(argv[1], "ctl")) {
		if (argc < 3)
			goto usage;
		fd = ctl_open_dev();
		if (!strcmp(argv[2], "init")) {
			memset(&ir, 0, sizeof(ir));
			ir.version = 0x01000100;
			ir.itmask = 0x0000000f;
			ir.busopt = 0xf << 12;
			rc = ioctl(fd, FWOIOC_INIT, &ir);
			printf("CTL_INIT rc=%d fwohci_init rc=%u "
			    "nisodma=%u\n", rc, ir.rc, ir.nisodma_out);
		} else if (!strcmp(argv[2], "dbinit") && argc >= 10) {
			memset(&db, 0, sizeof(db));
			db.ch = strtoul(argv[3], 0, 0);
			db.ndesc = strtoul(argv[4], 0, 0);
			db.ndb = strtoul(argv[5], 0, 0);
			db.psize = strtoul(argv[6], 0, 0);
			db.parent = strtoul(argv[7], 0, 0);
			db.elem = strtoul(argv[8], 0, 0);
			db.elem2 = strtoul(argv[9], 0, 0);
			rc = ioctl(fd, FWOIOC_DBINIT, &db);
			printf("CTL_DBINIT rc=%d dbinit.rc=%u ndesc=%u "
			    "ndb=%u esize=%u ssize=%u nseg=%u "
			    "seg0=0x%llx seglast=0x%llx elem%u=0x%llx "
			    "bus=0x%llx past=0x%llx elem2=%u va=0x%llx "
			    "bus2=0x%llx eps=%u\n",
			    rc, db.rc, db.o_ndesc, db.o_ndb, db.esize,
			    db.ssize, db.nseg,
			    (unsigned long long)db.seg0_va,
			    (unsigned long long)db.seglast_va, db.elem,
			    (unsigned long long)db.elem_va,
			    (unsigned long long)db.elem_bus,
			    (unsigned long long)db.past_va, db.elem2,
			    (unsigned long long)db.elem2_va,
			    (unsigned long long)db.elem2_bus,
			    db.elems_per_seg);
		} else if (!strcmp(argv[2], "descset") && argc >= 5) {
			int i;
			memset(&ds, 0, sizeof(ds));
			ds.elem = strtoul(argv[3], 0, 0);
			ds.canary = strtoul(argv[4], 0, 0);
			ds.n = 8;
			for (i = 0; i < 8; i++) {
				/* INPUT_MORE | KEY_ST3 | INTR_TRUE | cnt */
				ds.d[i].cmd = 0x20000000 | (3u << 24) |
				    (1u << 20) | (i + 1);
				ds.d[i].addr = 0xAA000000u + i;
				ds.d[i].depend = 0xBB000000u + i;
				ds.d[i].res = 0x00110000u | (i + 1);
			}
			rc = ioctl(fd, FWOIOC_DESCSET, &ds);
			printf("CTL_DESCSET rc=%d ds.rc=%u elem=%u "
			    "canary=%u\n", rc, ds.rc, ds.elem, ds.canary);
		} else if (!strcmp(argv[2], "regwr") && argc >= 5) {
			memset(&rr, 0, sizeof(rr));
			rr.addr = strtoul(argv[3], 0, 0);
			rr.data = strtoul(argv[4], 0, 0);
			rc = ioctl(fd, FWOIOC_REGWR, &rr);
			printf("CTL_REGWR rc=%d MMIO[0x%02x]=0x%08x\n",
			    rc, rr.addr, rr.data);
		} else if (!strcmp(argv[2], "regrd") && argc >= 4) {
			memset(&rr, 0, sizeof(rr));
			rr.addr = strtoul(argv[3], 0, 0);
			rc = ioctl(fd, FWOIOC_REGRD, &rr);
			printf("CTL_REGRD rc=%d MMIO[0x%02x]=0x%08x\n",
			    rc, rr.addr, rr.data);
		} else if (!strcmp(argv[2], "phyrd")) {
			int i;
			memset(&rr, 0, sizeof(rr));
			rc = ioctl(fd, FWOIOC_PHYRD, &rr);
			printf("CTL_PHYRD rc=%d phy:", rc);
			for (i = 0; i < 16; i++)
				printf(" %02x", rr.phy[i]);
			printf("\n");
		} else if (!strcmp(argv[2], "rxen") && argc >= 4) {
			memset(&ar, 0, sizeof(ar));
			ar.mode = strtoul(argv[3], 0, 0);
			rc = ioctl(fd, FWOIOC_RXEN, &ar);
			printf("CTL_RXEN rc=%d ch=%u rx_enable rc=%u\n",
			    rc, ar.mode, ar.rc);
		} else if (!strcmp(argv[2], "arcv") && argc >= 7) {
			memset(&ar, 0, sizeof(ar));
			ar.mode = strtoul(argv[3], 0, 0);
			ar.F = strtoul(argv[4], 0, 0);
			ar.tcode = strtoul(argv[5], 0, 0);
			ar.inj_offset = strtoul(argv[6], 0, 0);
			rc = ioctl(fd, FWOIOC_ARCV, &ar);
			printf("CTL_ARCV rc=%d arcv.rc=%u mode=%u F=%u "
			    "tcode=%u rlen_old=%u after: buf_offset=%d "
			    "pdb_tr=%u\n", rc, ar.rc, ar.mode, ar.F,
			    ar.tcode, ar.rlen_old_obs, ar.buf_offset_after,
			    ar.pdb_tr_set);
		} else if (!strcmp(argv[2], "memeat") && argc >= 5) {
			memset(&mm, 0, sizeof(mm));
			mm.free_pages = strtoul(argv[3], 0, 0);
			mm.ch = strtoul(argv[4], 0, 0);
			rc = ioctl(fd, FWOIOC_MEMEAT, &mm);
			printf("CTL_MEMEAT rc=%d ate=%u freed=%u "
			    "survived=%u\n", rc, mm.ate, mm.freed,
			    mm.survived);
		} else if (!strcmp(argv[2], "kread") && argc >= 4) {
			memset(&kr, 0, sizeof(kr));
			kr.kaddr = strtoull(argv[3], 0, 0);
			rc = ioctl(fd, FWOIOC_KREAD, &kr);
			printf("CTL_KREAD rc=%d [0x%llx] = %08x %08x "
			    "%08x %08x\n", rc,
			    (unsigned long long)kr.kaddr,
			    kr.data[0], kr.data[1], kr.data[2], kr.data[3]);
		} else if (!strcmp(argv[2], "state") && argc >= 4) {
			memset(&st, 0, sizeof(st));
			st.ch = strtoul(argv[3], 0, 0);
			rc = ioctl(fd, FWOIOC_STATE, &st);
			printf("CTL_STATE rc=%d ch=%u flags=%x ndb=%u "
			    "ndesc=%u buf_offset=%d pdb_tr=%u top=%u\n",
			    rc, st.ch, st.flags, st.ndb, st.ndesc,
			    st.buf_offset, st.pdb_tr_set, st.top_set);
		} else
			goto usage;
		close(fd);
		return (0);
	}

	fd = fw_open_dev();

	if (!strcmp(argv[1], "wr") && argc >= 4) {
		r.addr = strtoul(argv[2], 0, 16);
		r.data = strtoul(argv[3], 0, 16);
		rc = ioctl(fd, FW_FWOHCI_WRREG, &r);
		printf("FWOHCI_WRREG(0x%02lx, 0x%08lx) rc=%d errno=%d "
		    "readback=0x%08lx\n", r.addr, r.data, rc, errno, r.data);
	} else if (!strcmp(argv[1], "rd") && argc >= 3) {
		r.addr = strtoul(argv[2], 0, 16);
		r.data = 0;
		rc = ioctl(fd, FW_FWOHCI_RDREG, &r);
		printf("FWOHCI_RDREG(0x%02lx) rc=%d errno=%d data=0x%08lx\n",
		    r.addr, rc, errno, r.data);
	} else if (!strcmp(argv[1], "phyw") && argc >= 4) {
		r.addr = strtoul(argv[2], 0, 0);
		r.data = strtoul(argv[3], 0, 0);
		rc = ioctl(fd, FW_FWOHCI_WRPHYREG, &r);
		printf("FWOHCI_WRPHYREG(%lu, 0x%02lx) rc=%d errno=%d\n",
		    r.addr, r.data, rc, errno);
	} else if (!strcmp(argv[1], "phyr") && argc >= 3) {
		r.addr = strtoul(argv[2], 0, 0);
		r.data = 0;
		rc = ioctl(fd, FW_FWOHCI_RDPHYREG, &r);
		printf("FWOHCI_RDPHYREG(%lu) rc=%d errno=%d data=0x%02lx\n",
		    r.addr, rc, errno, r.data);
	} else if (!strcmp(argv[1], "dumpdma") && argc >= 3) {
		dmach = strtoul(argv[2], 0, 0);
		rc = ioctl(fd, FW_DUMPDMA, &dmach);
		printf("DUMPDMA(%u) rc=%d errno=%d\n", dmach, rc, errno);
	} else
		goto usage;

	close(fd);
	return (0);
}