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)
 bfgminer_CPPFLAGS += $(hidapi_CFLAGS)
 endif
 endif
 
 
+if NEED_BFG_LOWL_PCI
+bfgminer_SOURCES += lowl-pci.c lowl-pci.h
+endif
+
 bin_PROGRAMS += bfgminer-rpc
 bin_PROGRAMS += bfgminer-rpc
 bfgminer_rpc_SOURCES = api-example.c
 bfgminer_rpc_SOURCES = api-example.c
 bfgminer_rpc_LDADD = @WS2_LIBS@
 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
 	--with-system-libblkmaker  Use system libblkmaker rather than bundled one
 	                           (default disabled)
 	                           (default disabled)
 	--with-udevrulesdir=DIR Install udev rules into this directory
 	--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-sensors       Build with libsensors monitoring (default enabled)
 	--without-curses        Compile support for curses TUI (default enabled)
 	--without-curses        Compile support for curses TUI (default enabled)
 	--without-libmicrohttpd Compile support for libmicrohttpd getwork server
 	--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.
 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
 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_HEADER_STDC
 AC_CHECK_HEADERS(syslog.h)
 AC_CHECK_HEADERS(syslog.h)
 AC_CHECK_HEADERS([sys/epoll.h])
 AC_CHECK_HEADERS([sys/epoll.h])
+AC_CHECK_HEADERS([sys/mman.h])
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_HEADERS([sys/file.h])
 AC_CHECK_HEADERS([sys/file.h])
 AC_CHECK_HEADERS([linux/spi/spidev.h])
 AC_CHECK_HEADERS([linux/spi/spidev.h])
@@ -113,6 +114,7 @@ AC_CHECK_FUNCS([sleep])
 
 
 AC_FUNC_ALLOCA
 AC_FUNC_ALLOCA
 
 
+lowllist=
 driverlist=
 driverlist=
 algolist=SHA256d
 algolist=SHA256d
 optlist=
 optlist=
@@ -124,6 +126,7 @@ need_dynclock=no
 need_lowl_vcom=no
 need_lowl_vcom=no
 need_lowlevel=no
 need_lowlevel=no
 need_lowl_hid=no
 need_lowl_hid=no
+need_lowl_pci=no
 need_lowl_usb=no
 need_lowl_usb=no
 have_cygwin=false
 have_cygwin=false
 have_win32=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"
 driverlist="$driverlist cpu/cpumining"
 cpumining="no"
 cpumining="no"
 
 
@@ -371,6 +404,10 @@ AC_ARG_ENABLE([bitforce],
 	)
 	)
 if test "x$bitforce" = xyes; then
 if test "x$bitforce" = xyes; then
 	AC_DEFINE([USE_BITFORCE], [1], [Defined to 1 if BitForce support is wanted])
 	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
 	need_lowl_vcom=yes
 	has_fpga=yes
 	has_fpga=yes
 	has_asic=yes
 	has_asic=yes
@@ -975,15 +1012,32 @@ if test "x$opencl$need_lowl_hid" = xnono; then
 	DLOPEN_FLAGS=""
 	DLOPEN_FLAGS=""
 fi
 fi
 
 
+lowllist="$lowllist hid/need_lowl_hid"
 if test x$need_lowl_hid = xyes; then
 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])
 	AC_DEFINE([NEED_BFG_LOWL_HID], [1], [Defined to 1 if lowlevel hid drivers are being used])
 	need_lowlevel=yes
 	need_lowlevel=yes
 fi
 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
 if test x$need_lowl_usb = xyes; then
 	need_lowlevel=yes
 	need_lowlevel=yes
 fi
 fi
 
 
+lowllist="$lowllist vcom/need_lowl_vcom"
 if test x$need_lowl_vcom = xyes; then
 if test x$need_lowl_vcom = xyes; then
 	need_lowlevel=yes
 	need_lowlevel=yes
 fi
 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_DYNCLOCK], [test x$need_dynclock = xyes])
 AM_CONDITIONAL([NEED_BFG_LOWL_VCOM], [test x$need_lowl_vcom = 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_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([NEED_BFG_LOWLEVEL], [test x$need_lowlevel = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = 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])
 	AC_DEFINE_UNQUOTED(AS_TR_CPP([BFG_$2]),["$_yeslist"],[List of drivers being built])
 	$2_print="  Enabled..$1..:${_yeslist}~  Disabled.$1..:${_nolist}${_enableactions}"
 	$2_print="  Enabled..$1..:${_yeslist}~  Disabled.$1..:${_nolist}${_enableactions}"
 ])
 ])
+BFG_PRINT_LIST([Lowlevel..],[lowllist])
 BFG_PRINT_LIST([Drivers...],[driverlist])
 BFG_PRINT_LIST([Drivers...],[driverlist])
 BFG_PRINT_LIST([Algorithms],[algolist])
 BFG_PRINT_LIST([Algorithms],[algolist])
 BFG_PRINT_LIST([Options...],[optlist])
 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 "  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 "  Installation.prefix..: $prefix"
 echo
 echo
+echo "${lowllist_print}" | tr '~' '\n'
 echo "${driverlist_print}" | tr '~' '\n'
 echo "${driverlist_print}" | tr '~' '\n'
 echo "${algolist_print}" | tr '~' '\n'
 echo "${algolist_print}" | tr '~' '\n'
 echo "${optlist_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);
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
 #endif
 #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
 #ifdef NEED_BFG_LOWL_VCOM
 	devinfo_mid_list = lowl_vcom.devinfo_scan();
 	devinfo_mid_list = lowl_vcom.devinfo_scan();
 	LL_CONCAT(devinfo_list, devinfo_mid_list);
 	LL_CONCAT(devinfo_list, devinfo_mid_list);

+ 3 - 0
lowlevel.h

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

+ 1 - 0
miner.c

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

+ 33 - 0
util.c

@@ -2859,6 +2859,39 @@ void notifier_init(notifier_t pipefd)
 #endif
 #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)
 void notifier_wake(notifier_t fd)
 {
 {
 	if (fd[1] == INVSOCK)
 	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 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];
 typedef SOCKETTYPE notifier_t[2];
 extern void notifier_init(notifier_t);
 extern void notifier_init(notifier_t);
 extern void notifier_wake(notifier_t);
 extern void notifier_wake(notifier_t);

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