Browse Source

Merge branch 'monarch' into bfgminer

Luke Dashjr 12 years ago
parent
commit
73e67ff8ab
12 changed files with 1068 additions and 171 deletions
  1. 4 0
      Makefile.am
  2. 4 0
      README
  3. 34 0
      README.ASIC
  4. 57 0
      configure.ac
  5. 364 171
      driver-bitforce.c
  6. 493 0
      lowl-pci.c
  7. 68 0
      lowl-pci.h
  8. 5 0
      lowlevel.c
  9. 3 0
      lowlevel.h
  10. 1 0
      miner.c
  11. 33 0
      util.c
  12. 2 0
      util.h

+ 4 - 0
Makefile.am

@@ -299,6 +299,10 @@ bfgminer_SOURCES += lowl-hid.c lowl-hid.h
 bfgminer_CPPFLAGS += $(hidapi_CFLAGS)
 endif
 
+if NEED_BFG_LOWL_PCI
+bfgminer_SOURCES += lowl-pci.c lowl-pci.h
+endif
+
 bin_PROGRAMS += bfgminer-rpc
 bfgminer_rpc_SOURCES = api-example.c
 bfgminer_rpc_LDADD = @WS2_LIBS@

+ 4 - 0
README

@@ -160,6 +160,10 @@ BFGMiner specific configuration options:
 	--with-system-libblkmaker  Use system libblkmaker rather than bundled one
 	                           (default disabled)
 	--with-udevrulesdir=DIR Install udev rules into this directory
+	--without-uio           Compile support for PCI devices via Linux UIO
+	                        interface (default enabled)
+	--without-vfio          Compile support for PCI devices via Linux VFIO
+	                        interface (default enabled)
 	--without-sensors       Build with libsensors monitoring (default enabled)
 	--without-curses        Compile support for curses TUI (default enabled)
 	--without-libmicrohttpd Compile support for libmicrohttpd getwork server

+ 34 - 0
README.ASIC

@@ -174,6 +174,40 @@ make AR=arm-angstrom-linux-gnueabi-ar
 
 BFGMiner has also been incorporated into an unofficial firmware by uski01 called Bertmod this can be found on the kncminer forum.
 
+
+MONARCH
+-------
+
+The Butterfly Labs Monarch devices can be used as either USB devices, or in a
+PCI-Express slot. As USB devices, they are essentially plug-and-play. If you
+wish to use them via PCI-Express, however, you must first load the proper
+driver. BFGMiner can work with either Linux uio (2.6.23+, requires root access)
+or Linux vfio (3.6+, requires IOMMU support).
+
+To enable uio on your cards, you may need to do:
+    sudo modprobe uio_pci_generic
+    echo 1cf9 0001 | sudo tee /sys/bus/pci/drivers/uio_pci_generic/new_id
+
+Enabling vfio is similar, but more complicated, but allows you to run BFGMiner
+without root privileges. First, load the kernel module and tell it to accept the
+Monarch:
+    sudo modprobe vfio-pci
+    echo 1cf9 0001 | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id
+Next, identify what the device ids are for your card(s):
+    lspci -D | grep 1cf9  # the first number of each line is the device id
+From that, you can identify its IOMMU group, and list all devices sharing that
+group:
+    readlink /sys/bus/pci/devices/<device_id>/iommu_group
+    ls /sys/kernel/iommu_groups/<iommu_group>/devices/
+All of the devices listed (other than the Monarch), if any, will need to be
+disabled and unbound! To do that, use:
+    echo <device_id> | sudo tee /sys/bus/pci/devices/<device_id>/driver/unbind
+If you want to run BFGMiner as a normal user:
+    chown <username> /dev/vfio/<iommu_group>
+Depending on your system, you may also need to do:
+    echo 1 | sudo tee /sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts
+
+
 ---
 
 This code is provided entirely free of charge by the programmer in his spare

+ 57 - 0
configure.ac

@@ -79,6 +79,7 @@ dnl Checks for header files.
 AC_HEADER_STDC
 AC_CHECK_HEADERS(syslog.h)
 AC_CHECK_HEADERS([sys/epoll.h])
+AC_CHECK_HEADERS([sys/mman.h])
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_HEADERS([sys/file.h])
 AC_CHECK_HEADERS([linux/spi/spidev.h])
@@ -113,6 +114,7 @@ AC_CHECK_FUNCS([sleep])
 
 AC_FUNC_ALLOCA
 
+lowllist=
 driverlist=
 algolist=SHA256d
 optlist=
@@ -124,6 +126,7 @@ need_dynclock=no
 need_lowl_vcom=no
 need_lowlevel=no
 need_lowl_hid=no
+need_lowl_pci=no
 need_lowl_usb=no
 have_cygwin=false
 have_win32=false
@@ -218,6 +221,36 @@ AC_CHECK_DECL([HASH_ITER],[
 ])
 
 
+lowl_pci=no
+if test "x$ac_cv_header_sys_mman_h" = "xyes"; then
+	AC_ARG_WITH([uio],
+		[AC_HELP_STRING([--without-uio],[Compile support for PCI devices via Linux UIO interface (default enabled)])],
+		[uio=$withval],
+		[uio=yes])
+	AC_ARG_WITH([vfio],
+		[AC_HELP_STRING([--without-vfio],[Compile support for PCI devices via Linux VFIO interface (default enabled)])],
+		[vfio=$enableval],
+		[vfio=auto])
+	if test "x$vfio" != "xno"; then
+		AC_CHECK_HEADER([linux/vfio.h],[
+			vfio=yes
+		],[
+			if test "x$vfio" = "xyes"; then
+				AC_MSG_ERROR([Unable to find linux/vfio.h])
+			elif test "x$uio" = "xyes"; then
+				AC_MSG_WARN([linux/vfio.h not found; PCI device support will require UIO (and root access)])
+			else
+				AC_MSG_WARN([linux/vfio.h not found; PCI device support will not be available])
+			fi
+			vfio=no
+		])
+	fi
+	if test "x$vfio$uio" != xnono; then
+		lowl_pci=yes
+	fi
+fi
+
+
 driverlist="$driverlist cpu/cpumining"
 cpumining="no"
 
@@ -371,6 +404,10 @@ AC_ARG_ENABLE([bitforce],
 	)
 if test "x$bitforce" = xyes; then
 	AC_DEFINE([USE_BITFORCE], [1], [Defined to 1 if BitForce support is wanted])
+	driverlist="$driverlist bitforce:uio/ac_cv_header_sys_mman_h"
+	if test "x$lowl_pci" = "xyes"; then
+		need_lowl_pci=yes
+	fi
 	need_lowl_vcom=yes
 	has_fpga=yes
 	has_asic=yes
@@ -975,15 +1012,32 @@ if test "x$opencl$need_lowl_hid" = xnono; then
 	DLOPEN_FLAGS=""
 fi
 
+lowllist="$lowllist hid/need_lowl_hid"
 if test x$need_lowl_hid = xyes; then
 	AC_DEFINE([NEED_BFG_LOWL_HID], [1], [Defined to 1 if lowlevel hid drivers are being used])
 	need_lowlevel=yes
 fi
 
+lowllist="$lowllist pci/need_lowl_pci"
+if test x$need_lowl_pci = xyes; then
+	AC_DEFINE([NEED_BFG_LOWL_PCI], [1], [Defined to 1 if lowlevel PCI drivers are being used])
+	need_lowlevel=yes
+	lowllist="$lowllist pci:uio/uio"
+	if test x$uio = xyes; then
+		AC_DEFINE([USE_UIO], [1], [Defined to 1 if lowlevel PCI drivers should support UIO])
+	fi
+	lowllist="$lowllist pci:vfio/vfio"
+	if test x$vfio = xyes; then
+		AC_DEFINE([USE_VFIO], [1], [Defined to 1 if lowlevel PCI drivers should support VFIO])
+	fi
+fi
+
+lowllist="$lowllist usb/need_lowl_usb"
 if test x$need_lowl_usb = xyes; then
 	need_lowlevel=yes
 fi
 
+lowllist="$lowllist vcom/need_lowl_vcom"
 if test x$need_lowl_vcom = xyes; then
 	need_lowlevel=yes
 fi
@@ -1168,6 +1222,7 @@ AM_CONDITIONAL([NEED_BFG_BINLOADER], [test x$need_binloader = xyes])
 AM_CONDITIONAL([NEED_DYNCLOCK], [test x$need_dynclock = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_VCOM], [test x$need_lowl_vcom = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_HID], [test x$need_lowl_hid = xyes])
+AM_CONDITIONAL([NEED_BFG_LOWL_PCI], [test x$need_lowl_pci = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWLEVEL], [test x$need_lowlevel = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
@@ -1532,6 +1587,7 @@ EOF
 	AC_DEFINE_UNQUOTED(AS_TR_CPP([BFG_$2]),["$_yeslist"],[List of drivers being built])
 	$2_print="  Enabled..$1..:${_yeslist}~  Disabled.$1..:${_nolist}${_enableactions}"
 ])
+BFG_PRINT_LIST([Lowlevel..],[lowllist])
 BFG_PRINT_LIST([Drivers...],[driverlist])
 BFG_PRINT_LIST([Algorithms],[algolist])
 BFG_PRINT_LIST([Options...],[optlist])
@@ -1582,6 +1638,7 @@ echo "  CFLAGS...............: "`wordfilter "$CPPFLAGS $AUTOSCAN_CPPFLAGS $NCURS
 echo "  LDFLAGS..............: "`wordfilter "$LDFLAGS $AUTOSCAN_LIBS $PTHREAD_FLAGS $libblkmaker_LDFLAGS $PTHREAD_LIBS $LIBS $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $LIBUSB_LIBS $RT_LIBS $sensors_LIBS $libblkmaker_LIBS"`
 echo "  Installation.prefix..: $prefix"
 echo
+echo "${lowllist_print}" | tr '~' '\n'
 echo "${driverlist_print}" | tr '~' '\n'
 echo "${algolist_print}" | tr '~' '\n'
 echo "${optlist_print}" | tr '~' '\n'

File diff suppressed because it is too large
+ 364 - 171
driver-bitforce.c


+ 493 - 0
lowl-pci.c

@@ -0,0 +1,493 @@
+/*
+ * Copyright 2014 Luke Dashjr
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "config.h"
+
+#if defined(USE_VFIO) || defined(USE_UIO)
+#	define USE_LOWL_PCI_MMAP
+#	define USE_LOWL_PCI_DATA_WRAPPERS
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <utlist.h>
+
+#ifdef USE_VFIO
+#include <linux/vfio.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef USE_LOWL_PCI_MMAP
+#include <sys/mman.h>
+#endif
+
+#include "logging.h"
+#include "lowlevel.h"
+#include "lowl-pci.h"
+#include "miner.h"
+#include "util.h"
+
+static
+struct lowlevel_device_info *pci_devinfo_scan()
+{
+	struct lowlevel_device_info *devinfo_list = NULL, *info;
+	struct dirent *de;
+	char filename[0x100] = "/sys/bus/pci/devices", buf[0x10];
+	DIR * const D = opendir(filename);
+	if (!D)
+		return 0;
+	char * const p = &filename[strlen(filename)], *devid;
+	const size_t psz = sizeof(filename) - (p - filename);
+	uint32_t vid, pid;
+	size_t d_name_len;
+	while ( (de = readdir(D)) )
+	{
+		d_name_len = strlen(de->d_name);
+		snprintf(p, psz, "/%s/vendor", de->d_name);
+		if (!bfg_slurp_file(buf, sizeof(buf), filename))
+			continue;
+		vid = strtoll(buf, NULL, 0);
+		snprintf(p, psz, "/%s/device", de->d_name);
+		if (!bfg_slurp_file(buf, sizeof(buf), filename))
+			continue;
+		pid = strtoll(buf, NULL, 0);
+		devid = malloc(4 + d_name_len + 1);
+		sprintf(devid, "pci:%s", de->d_name);
+		
+		info = malloc(sizeof(struct lowlevel_device_info));
+		*info = (struct lowlevel_device_info){
+			.lowl = &lowl_pci,
+			.devid = devid,
+			.path = strdup(de->d_name),
+			.vid = vid,
+			.pid = pid,
+		};
+		
+		LL_PREPEND(devinfo_list, info);
+	}
+	closedir(D);
+	return devinfo_list;
+}
+
+struct lowl_pci_interface {
+	struct lowl_pci_handle *(*open)(const char *path, const struct _lowl_pci_config *);
+	void (*close)(struct lowl_pci_handle *);
+	const uint32_t *(*get_words)(struct lowl_pci_handle *, void *buf, size_t words, int bar, off_t);
+	const void *(*get_data)(struct lowl_pci_handle *, void *buf, size_t, int bar, off_t);
+	bool (*set_words)(struct lowl_pci_handle *, const uint32_t *, size_t, int bar, off_t);
+	bool (*set_data)(struct lowl_pci_handle *, const void *, size_t, int bar, off_t);
+};
+
+struct lowl_pci_handle {
+	const char *path;
+	const struct lowl_pci_interface *lpi;
+#ifdef USE_VFIO
+	int fd[3];
+	off_t baroff[6];
+#endif
+#ifdef USE_LOWL_PCI_MMAP
+	uint32_t *bar[6];
+	size_t barsz[6];
+#endif
+};
+
+#ifdef USE_LOWL_PCI_MMAP
+static
+void lowl_pci_close_mmap(struct lowl_pci_handle * const lph)
+{
+	for (int i = 0; i < 6; ++i)
+		if (lph->bar[i])
+			munmap(lph->bar[i], lph->barsz[i]);
+	free(lph);
+}
+
+static
+const uint32_t *lowl_pci_get_words_mmap(struct lowl_pci_handle * const lph, void * const buf, const size_t words, const int bar, const off_t offset)
+{
+	return &lph->bar[bar][offset];
+}
+
+static
+bool lowl_pci_set_words_mmap(struct lowl_pci_handle * const lph, const uint32_t *buf, const size_t words, const int bar, const off_t offset)
+{
+	uint32_t *dest = &lph->bar[bar][offset];
+	for (int i = 0; i < words; ++i)
+		*(dest++) = *(buf++);
+	return true;
+}
+
+static
+int _file_mode_to_mmap_prot(const int mode)
+{
+	switch (mode)
+	{
+		case O_RDONLY:
+			return PROT_READ;
+		case O_WRONLY:
+			return PROT_WRITE;
+		case O_RDWR:
+			return PROT_READ | PROT_WRITE;
+		default:
+			return -1;
+	}
+}
+#endif
+
+#ifdef USE_LOWL_PCI_DATA_WRAPPERS
+static
+const void *lowl_pci_get_data_from_words(struct lowl_pci_handle * const lph, void * const bufp, const size_t sz, const int bar, const off_t offset)
+{
+	uint8_t * const buf = bufp;
+	const off_t offset32 = offset / 4;
+	const off_t offset8  = offset % 4;
+	const size_t words = (sz + offset8 + 3) / 4;
+	const uint32_t * const wdata = lowl_pci_get_words(lph, buf, words, bar, offset32);
+	swap32tobe(buf, wdata, words);
+	return &buf[offset8];
+}
+
+static
+bool lowl_pci_set_data_in_words(struct lowl_pci_handle * const lph, const void * const bufp, size_t sz, const int bar, const off_t offset)
+{
+	const uint8_t *buf = bufp;
+	const off_t offset32 = offset / 4;
+	off_t offset8  = offset % 4;
+	const size_t words = (sz + offset8 + 3) / 4;
+	uint32_t wdata[words], *wdp = wdata;
+	if (offset8)
+	{
+		const uint32_t * const p = lowl_pci_get_words(lph, wdata, 1, bar, offset32);
+		if (unlikely(!p))
+			return false;
+		wdata[0] = *p >> (32 - (8 * offset8));
+	}
+	for ( ; sz; --sz)
+	{
+		*wdp = (*wdp << 8) | *(buf++);
+		if (++offset8 == 4)
+		{
+			offset8 = 0;
+			++wdp;
+		}
+	}
+	if (offset8)
+	{
+		uint32_t u;
+		const uint32_t * const p = lowl_pci_get_words(lph, &u, 1, bar, offset32 + words - 1);
+		if (unlikely(!p))
+			return false;
+		const int n = 32 - (8 * offset8);
+		wdp[0] <<= n;
+		wdp[0] |= *p & ((1 << n) - 1);
+	}
+	return lowl_pci_set_words(lph, wdata, words, bar, offset32);
+}
+#endif
+
+#ifdef USE_UIO
+static const struct lowl_pci_interface lpi_uio;
+
+static
+void *_uio_mmap_bar(const char * const path, const int bar, const size_t sz, const int prot)
+{
+	char buf[0x100];
+	snprintf(buf, sizeof(buf), "/sys/bus/pci/devices/%s/resource%d", path, bar);
+	const int fd = open(buf, O_RDWR);
+	if (fd == -1)
+		return MAP_FAILED;
+	void * const rv = mmap(NULL, sz, prot, MAP_SHARED, fd, 0);
+	close(fd);
+	return rv;
+}
+
+struct lowl_pci_handle *lowl_pci_open_uio(const char * const path, const struct _lowl_pci_config * const barcfgs)
+{
+	struct lowl_pci_handle * const lph = malloc(sizeof(*lph));
+	*lph = (struct lowl_pci_handle){
+		.path = path,
+		.lpi = &lpi_uio,
+	};
+	for (const struct _lowl_pci_config *barcfg = barcfgs; barcfg->bar != -1; ++barcfg)
+	{
+		const int barno = barcfg->bar;
+		const int prot = _file_mode_to_mmap_prot(barcfg->mode);
+		if (unlikely(prot == -1))
+			goto err;
+		lph->bar[barno] = _uio_mmap_bar(path, barno, barcfg->sz, prot);
+		lph->barsz[barno] = barcfg->sz;
+		if (lph->bar[barno] == MAP_FAILED)
+		{
+			applog(LOG_ERR, "mmap %s bar %d failed", path, barno);
+			goto err;
+		}
+	}
+	return lph;
+
+err:
+	for (int i = 0; i < 6; ++i)
+		if (lph->bar[i])
+			munmap(lph->bar[i], lph->barsz[i]);
+	free(lph);
+	return NULL;
+}
+
+static const struct lowl_pci_interface lpi_uio = {
+	.open = lowl_pci_open_uio,
+	.close = lowl_pci_close_mmap,
+	.get_words = lowl_pci_get_words_mmap,
+	.get_data  = lowl_pci_get_data_from_words,
+	.set_words = lowl_pci_set_words_mmap,
+	.set_data  = lowl_pci_set_data_in_words,
+};
+#endif
+
+#ifdef USE_VFIO
+static const struct lowl_pci_interface lpi_vfio;
+
+#define _VFIO_ACCESS_BAR_PROBLEM ((void*)&lpi_vfio)
+static
+void *_vfio_access_bar(const int device, const int bar, const size_t sz, const int prot, off_t *out_offset)
+{
+	struct vfio_region_info region_info = { .argsz = sizeof(region_info) };
+	switch (bar)
+	{
+#define _BARCASE(n)  \
+		case n:  \
+			region_info.index = VFIO_PCI_BAR ## n ## _REGION_INDEX;  \
+			break;
+		_BARCASE(0) _BARCASE(1) _BARCASE(2) _BARCASE(3)
+		_BARCASE(4) _BARCASE(5)
+#undef _BARCASE
+		default:
+			return _VFIO_ACCESS_BAR_PROBLEM;
+	}
+	if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, &region_info))
+		applogr(_VFIO_ACCESS_BAR_PROBLEM, LOG_ERR, "%s: VFIO_DEVICE_GET_REGION_INFO failed", __func__);
+	if ((prot & PROT_READ ) && !(region_info.flags & VFIO_REGION_INFO_FLAG_READ ))
+		applogr(_VFIO_ACCESS_BAR_PROBLEM, LOG_ERR, "%s: region does not support %s", __func__, "read");
+	if ((prot & PROT_WRITE) && !(region_info.flags & VFIO_REGION_INFO_FLAG_WRITE))
+		applogr(_VFIO_ACCESS_BAR_PROBLEM, LOG_ERR, "%s: region does not support %s", __func__, "write");
+	if (region_info.size < sz)
+		applogr(_VFIO_ACCESS_BAR_PROBLEM, LOG_ERR, "%s: region is only %lu bytes (needed %lu)",
+		        __func__, (unsigned long)region_info.size, (unsigned long)sz);
+	
+	*out_offset = region_info.offset;
+	if (!(region_info.flags & VFIO_REGION_INFO_FLAG_MMAP))
+		return MAP_FAILED;
+	
+	return mmap(NULL, sz, prot, MAP_SHARED, device, region_info.offset);
+}
+
+struct lowl_pci_handle *lowl_pci_open_vfio(const char * const path, const struct _lowl_pci_config * const barcfgs)
+{
+	char buf[0x100], buf2[0x100];
+	ssize_t ss;
+	char *p;
+	int group = -1, device = -1;
+	off_t offset;
+	
+	struct lowl_pci_handle * const lph = malloc(sizeof(*lph));
+	*lph = (struct lowl_pci_handle){
+		.path = path,
+		.lpi = &lpi_vfio,
+	};
+	const char * const vfio_path = "/dev/vfio/vfio";
+	const int container = open(vfio_path, O_RDWR);
+	if (container == -1)
+	{
+		applog(LOG_ERR, "%s: Failed to open %s", __func__, vfio_path);
+		goto err;
+	}
+	{
+		const int vfio_ver = ioctl(container, VFIO_GET_API_VERSION);
+		if (vfio_ver != VFIO_API_VERSION)
+		{
+			applog(LOG_ERR, "%s: vfio API version mismatch (have=%d expect=%d)",
+			       __func__, vfio_ver, VFIO_API_VERSION);
+			goto err;
+		}
+	}
+	snprintf(buf, sizeof(buf), "/sys/bus/pci/devices/%s/iommu_group", path);
+	ss = readlink(buf, buf2, sizeof(buf2) - 1);
+	if (ss == -1)
+	{
+		applog(LOG_ERR, "%s: Failed to read %s", __func__, buf);
+		goto err;
+	}
+	buf2[ss] = '\0';
+	p = memrchr(buf2, '/', ss - 1);
+	if (p)
+		++p;
+	else
+		p = buf2;
+	snprintf(buf, sizeof(buf), "/dev/vfio/%s", p);
+	group = open(buf, O_RDWR);
+	if (group == -1)
+	{
+		applog(LOG_ERR, "%s: Failed to open %s", __func__, buf);
+		goto err;
+	}
+	struct vfio_group_status group_status = { .argsz = sizeof(group_status) };
+	if (ioctl(group, VFIO_GROUP_GET_STATUS, &group_status))
+	{
+		applog(LOG_ERR, "%s: VFIO_GROUP_GET_STATUS failed on iommu group %s", __func__, p);
+		goto err;
+	}
+	if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
+	{
+		applog(LOG_ERR, "%s: iommu group %s is not viable", __func__, p);
+		goto err;
+	}
+	if (ioctl(group, VFIO_GROUP_SET_CONTAINER, &container))
+	{
+		applog(LOG_ERR, "%s: VFIO_GROUP_SET_CONTAINER failed on iommu group %s", __func__, p);
+		goto err;
+	}
+	if (ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU))
+	{
+		applog(LOG_ERR, "%s: Failed to set type1 iommu on group %s", __func__, p);
+		goto err;
+	}
+	device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, path);
+	if (device == -1)
+	{
+		applog(LOG_ERR, "%s: Failed to get device fd for %s in group %s", __func__, path, p);
+		goto err;
+	}
+	for (const struct _lowl_pci_config *barcfg = barcfgs; barcfg->bar != -1; ++barcfg)
+	{
+		const int barno = barcfg->bar;
+		const int prot = _file_mode_to_mmap_prot(barcfg->mode);
+		if (unlikely(prot == -1))
+			goto err;
+		lph->bar[barno] = _vfio_access_bar(device, barno, barcfg->sz, prot, &offset);
+		lph->barsz[barno] = barcfg->sz;
+		if (lph->bar[barno] == _VFIO_ACCESS_BAR_PROBLEM)
+		{
+			applog(LOG_ERR, "%s: Accessing %s bar %d failed", __func__, path, barno);
+			goto err;
+		}
+		else
+		if (lph->bar[barno] == MAP_FAILED)
+			lph->bar[barno] = NULL;
+		lph->baroff[barno] = offset;
+	}
+	lph->fd[0] = device;
+	lph->fd[1] = group;
+	lph->fd[2] = container;
+	return lph;
+
+err:
+	for (int i = 0; i < 6; ++i)
+		if (lph->bar[i])
+			munmap(lph->bar[i], lph->barsz[i]);
+	if (device != -1)
+		close(device);
+	if (group != -1)
+		close(group);
+	if (container != -1)
+		close(container);
+	free(lph);
+	return NULL;
+}
+
+static
+void lowl_pci_close_vfio(struct lowl_pci_handle * const lph)
+{
+	close(lph->fd[0]);
+	close(lph->fd[1]);
+	close(lph->fd[2]);
+	lowl_pci_close_mmap(lph);
+}
+
+static
+const uint32_t *lowl_pci_get_words_vfio(struct lowl_pci_handle * const lph, void * const buf, const size_t words, const int bar, const off_t offset)
+{
+	if (lph->bar[bar])
+		return lowl_pci_get_words_mmap(lph, buf, words, bar, offset);
+	
+	const size_t sz = 4 * words;
+	if (unlikely(sz != pread(lph->fd[0], buf, sz, (4 * offset) + lph->baroff[bar])))
+		return NULL;
+	return buf;
+}
+
+static
+bool lowl_pci_set_words_vfio(struct lowl_pci_handle * const lph, const uint32_t *buf, const size_t words, const int bar, const off_t offset)
+{
+	if (lph->bar[bar])
+		return lowl_pci_set_words_mmap(lph, buf, words, bar, offset);
+	
+	const size_t sz = 4 * words;
+	if (unlikely(sz != pwrite(lph->fd[0], buf, sz, (4 * offset) + lph->baroff[bar])))
+		return false;
+	return true;
+}
+
+static const struct lowl_pci_interface lpi_vfio = {
+	.open = lowl_pci_open_vfio,
+	.close = lowl_pci_close_vfio,
+	.get_words = lowl_pci_get_words_vfio,
+	.get_data  = lowl_pci_get_data_from_words,
+	.set_words = lowl_pci_set_words_vfio,
+	.set_data  = lowl_pci_set_data_in_words,
+};
+#endif
+
+struct lowl_pci_handle *lowl_pci_open(const char * const path, const struct _lowl_pci_config * const barcfgs)
+{
+	return
+#ifdef USE_VFIO
+		lpi_vfio.open(path, barcfgs) ?:
+#endif
+#ifdef USE_UIO
+		lpi_uio.open(path, barcfgs) ?:
+#endif
+		false;
+}
+
+const uint32_t *lowl_pci_get_words(struct lowl_pci_handle * const lph, void * const buf, const size_t words, const int bar, const off_t offset)
+{
+	return lph->lpi->get_words(lph, buf, words, bar, offset);
+}
+
+const void *lowl_pci_get_data(struct lowl_pci_handle * const lph, void * const buf, const size_t sz, const int bar, const off_t offset)
+{
+	return lph->lpi->get_data(lph, buf, sz, bar, offset);
+}
+
+bool lowl_pci_set_words(struct lowl_pci_handle * const lph, const uint32_t * const buf, const size_t words, const int bar, const off_t offset)
+{
+	return lph->lpi->set_words(lph, buf, words, bar, offset);
+}
+
+bool lowl_pci_set_data(struct lowl_pci_handle * const lph, const void * const buf, const size_t sz, const int bar, const off_t offset)
+{
+	return lph->lpi->set_data(lph, buf, sz, bar, offset);
+}
+
+void lowl_pci_close(struct lowl_pci_handle * const lph)
+{
+	return lph->lpi->close(lph);
+}
+
+struct lowlevel_driver lowl_pci = {
+	.dname = "pci",
+	.devinfo_scan = pci_devinfo_scan,
+};

+ 68 - 0
lowl-pci.h

@@ -0,0 +1,68 @@
+#ifndef BFG_LOWL_PCI_H
+#define BFG_LOWL_PCI_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+struct lowl_pci_handle;
+
+struct _lowl_pci_config {
+	int bar;
+	size_t sz;
+	int mode;
+};
+extern struct lowl_pci_handle *lowl_pci_open(const char *, const struct _lowl_pci_config *);
+#define LP_BARINFO(...)  (struct _lowl_pci_config[]){__VA_ARGS__ { .bar = -1 }}
+#define LP_BAR(barno, size, mode)  {barno, size, mode}
+extern void lowl_pci_close(struct lowl_pci_handle *);
+
+// Don't assume buf is used in any specific way! Memory returned may be mmap'd (and thus change after call)
+extern const uint32_t *lowl_pci_get_words(struct lowl_pci_handle *, void *buf, size_t words, int bar, off_t);
+extern bool lowl_pci_set_words(struct lowl_pci_handle *, const uint32_t *, size_t, int bar, off_t);
+// buf passed to lowl_pci_get_data must have at least LOWL_PCI_GET_DATA_PADDING bytes more than size to read
+#define LOWL_PCI_GET_DATA_PADDING 6
+extern const void *lowl_pci_get_data(struct lowl_pci_handle *, void *buf, size_t, int bar, off_t);
+extern bool lowl_pci_set_data(struct lowl_pci_handle *, const void *, size_t, int bar, off_t);
+
+static inline
+uint32_t lowl_pci_get_word(struct lowl_pci_handle * const lph, const int bar, const off_t offset)
+{
+	uint32_t buf[1];
+	const uint32_t * const p = lowl_pci_get_words(lph, buf, 1, bar, offset);
+	if (!p)
+		return 0;
+	return *p;
+}
+
+static inline
+bool lowl_pci_set_word(struct lowl_pci_handle * const lph, const int bar, const off_t offset, const uint32_t val)
+{
+	return lowl_pci_set_words(lph, &val, 1, bar, offset);
+}
+
+static inline
+bool lowl_pci_read_words(struct lowl_pci_handle * const lph, void * const buf, const size_t words, const int bar, const off_t offset)
+{
+	const void * const p = lowl_pci_get_words(lph, buf, words, bar, offset);
+	if (!p)
+		return false;
+	if (buf != p)
+		memmove(buf, p, words * 4);
+	return true;
+}
+
+static inline
+bool lowl_pci_read_data(struct lowl_pci_handle * const lph, void * const buf, const size_t sz, const int bar, const off_t offset)
+{
+	const void * const p = lowl_pci_get_data(lph, buf, sz, bar, offset);
+	if (!p)
+		return false;
+	if (buf != p)
+		memmove(buf, p, sz);
+	return true;
+}
+
+#endif

+ 5 - 0
lowlevel.c

@@ -109,6 +109,11 @@ struct lowlevel_device_info *lowlevel_scan()
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
 #endif
 	
+#ifdef NEED_BFG_LOWL_PCI
+	devinfo_mid_list = lowl_pci.devinfo_scan();
+	LL_CONCAT(devinfo_list, devinfo_mid_list);
+#endif
+	
 #ifdef NEED_BFG_LOWL_VCOM
 	devinfo_mid_list = lowl_vcom.devinfo_scan();
 	LL_CONCAT(devinfo_list, devinfo_mid_list);

+ 3 - 0
lowlevel.h

@@ -66,6 +66,9 @@ extern struct lowlevel_driver lowl_hid;
 #ifdef USE_NANOFURY
 extern struct lowlevel_driver lowl_mcp2210;
 #endif
+#ifdef NEED_BFG_LOWL_PCI
+extern struct lowlevel_driver lowl_pci;
+#endif
 #ifdef HAVE_LIBUSB
 extern struct lowlevel_driver lowl_usb;
 #else

+ 1 - 0
miner.c

@@ -2541,6 +2541,7 @@ extern const char *opt_argv0;
 static char *opt_verusage_and_exit(const char *extra)
 {
 	puts(packagename);
+	printf("  Lowlevel:%s\n", BFG_LOWLLIST);
 	printf("  Drivers:%s\n", BFG_DRIVERLIST);
 	printf("  Algorithms:%s\n", BFG_ALGOLIST);
 	printf("  Options:%s\n", BFG_OPTLIST);

+ 33 - 0
util.c

@@ -2859,6 +2859,39 @@ void notifier_init(notifier_t pipefd)
 #endif
 }
 
+
+void *bfg_slurp_file(void * const bufp, size_t bufsz, const char * const filename)
+{
+	char *buf = bufp;
+	FILE * const F = fopen(filename, "r");
+	if (!F)
+		goto err;
+	
+	if (!buf)
+	{
+		fseek(F, 0, SEEK_END);
+		const long filesz = ftell(F);
+		if (unlikely(filesz < 0))
+		{
+			fclose(F);
+			goto err;
+		}
+		rewind(F);
+		bufsz = filesz + 1;
+		buf = malloc(bufsz);
+	}
+	const size_t rsz = fread(buf, 1, bufsz - 1, F);
+	fclose(F);
+	buf[rsz] = '\0';
+	return buf;
+
+err:
+	if (buf)
+		buf[0] = '\0';
+	return NULL;
+}
+
+
 void notifier_wake(notifier_t fd)
 {
 	if (fd[1] == INVSOCK)

+ 2 - 0
util.h

@@ -181,6 +181,8 @@ enum bfg_strerror_type {
 };
 extern const char *bfg_strerror(int, enum bfg_strerror_type);
 
+extern void *bfg_slurp_file(void *buf, size_t bufsz, const char *filename);
+
 typedef SOCKETTYPE notifier_t[2];
 extern void notifier_init(notifier_t);
 extern void notifier_wake(notifier_t);

Some files were not shown because too many files changed in this diff