From 21820f61a8b77b564d487ff46508b39aebc98780 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jun 09 2020 19:58:38 +0000 Subject: import opae-1.4.1-11.el8 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..248f237 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/opae-1.4.1-1.tar.gz diff --git a/.opae.metadata b/.opae.metadata new file mode 100644 index 0000000..b53e237 --- /dev/null +++ b/.opae.metadata @@ -0,0 +1 @@ +ffe06afd07af73127a2b64426686e5ebd14bceb4 SOURCES/opae-1.4.1-1.tar.gz diff --git a/SOURCES/0001-Do-not-install-static-libraries.patch b/SOURCES/0001-Do-not-install-static-libraries.patch new file mode 100644 index 0000000..edcd427 --- /dev/null +++ b/SOURCES/0001-Do-not-install-static-libraries.patch @@ -0,0 +1,39 @@ +From 28a8f51964af5b439183f45fbd42fe13a17f74dc Mon Sep 17 00:00:00 2001 +From: Tom Rix +Date: Sun, 19 Apr 2020 12:43:20 -0400 +Subject: [PATCH] Do not install static libraries + +Development libraries must be shared libraries. +Internal libraries can be static libraries. +Do not install internal libraries. + +This effects libargsfilter.a which is only used by fpgainfo + +Signed-off-by: Tom Rix +--- + cmake/modules/OPAECompiler.cmake | 11 ----------- + 1 file changed, 11 deletions(-) + +diff --git a/cmake/modules/OPAECompiler.cmake b/cmake/modules/OPAECompiler.cmake +index dfbc553..4aa05cc 100644 +--- a/opae-libs/cmake/modules/OPAECompiler.cmake ++++ b/opae-libs/cmake/modules/OPAECompiler.cmake +@@ -346,15 +346,4 @@ function(opae_add_static_library) + + opae_coverage_build(TARGET ${OPAE_ADD_STATIC_LIBRARY_TARGET} SOURCE ${OPAE_ADD_STATIC_LIBRARY_SOURCE}) + +- if(OPAE_ADD_STATIC_LIBRARY_COMPONENT) +- if(OPAE_ADD_STATIC_LIBRARY_DESTINATION) +- set(dest ${OPAE_ADD_STATIC_LIBRARY_DESTINATION}) +- else(OPAE_ADD_STATIC_LIBRARY_DESTINATION) +- set(dest ${OPAE_LIB_INSTALL_DIR}) +- endif(OPAE_ADD_STATIC_LIBRARY_DESTINATION) +- +- install(TARGETS ${OPAE_ADD_STATIC_LIBRARY_TARGET} +- ARCHIVE DESTINATION ${dest} +- COMPONENT ${OPAE_ADD_STATIC_LIBRARY_COMPONENT}) +- endif(OPAE_ADD_STATIC_LIBRARY_COMPONENT) + endfunction() +-- +2.18.1 + diff --git a/SOURCES/0001-Fix-possible-buffer-overflow.patch b/SOURCES/0001-Fix-possible-buffer-overflow.patch new file mode 100644 index 0000000..7ff7d40 --- /dev/null +++ b/SOURCES/0001-Fix-possible-buffer-overflow.patch @@ -0,0 +1,31 @@ +From 5e058dfcc0ff0d945107d7318eef94db9fdea8b2 Mon Sep 17 00:00:00 2001 +From: Tom Rix +Date: Tue, 19 May 2020 09:10:09 -0400 +Subject: [PATCH] Fix possible buffer overflow + +readlink returns the number of bytes written. +If that fills the buffer, then the NULL assignment will overflow. +Reduce the buffer size parameter in readlink by 1 to +ensure there is space for the NULL. + +Signed-off-by: Tom Rix +--- + opae-libs/plugins/xfpga/sysfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/opae-libs/plugins/xfpga/sysfs.c b/opae-libs/plugins/xfpga/sysfs.c +index d82db4e3..bf7e3b3d 100644 +--- a/opae-libs/plugins/xfpga/sysfs.c ++++ b/opae-libs/plugins/xfpga/sysfs.c +@@ -1793,7 +1793,7 @@ fpga_result sysfs_sbdf_from_path(const char *sysfspath, int *s, int *b, int *d, + char rlpath[SYSFS_PATH_MAX]; + char *p; + +- res = readlink(sysfspath, rlpath, sizeof(rlpath)); ++ res = readlink(sysfspath, rlpath, sizeof(rlpath)-1); + if (-1 == res) { + OPAE_MSG("Can't read link %s (no driver?)", sysfspath); + return FPGA_NO_DRIVER; +-- +2.18.2 + diff --git a/SOURCES/0001-Import-fpgad-from-opae-legacy.patch b/SOURCES/0001-Import-fpgad-from-opae-legacy.patch new file mode 100644 index 0000000..3aae066 --- /dev/null +++ b/SOURCES/0001-Import-fpgad-from-opae-legacy.patch @@ -0,0 +1,6489 @@ +From 99928f8a8f671a7e6a139e014f4db55482d09d16 Mon Sep 17 00:00:00 2001 +From: Tom Rix +Date: Thu, 14 May 2020 09:50:28 -0400 +Subject: [PATCH] Import fpgad from opae-legacy + +fpgad is a useful tool that was removed in 1.4.0 +Reimport it from the opae-legacy, release/1.4.1 branch +Change the cmake build system to integrate back into build. + +Signed-off-by: Tom Rix +--- + tools/CMakeLists.txt | 1 + + tools/fpgad/CMakeLists.txt | 82 ++ + tools/fpgad/api/CMakeLists.txt | 46 + + tools/fpgad/api/device_monitoring.c | 73 ++ + tools/fpgad/api/device_monitoring.h | 46 + + tools/fpgad/api/logging.c | 132 ++ + tools/fpgad/api/logging.h | 37 + + tools/fpgad/api/opae_events_api.c | 239 ++++ + tools/fpgad/api/opae_events_api.h | 85 ++ + tools/fpgad/api/sysfs.c | 76 ++ + tools/fpgad/api/sysfs.h | 45 + + tools/fpgad/command_line.c | 516 ++++++++ + tools/fpgad/command_line.h | 84 ++ + tools/fpgad/config_file.c | 621 ++++++++++ + tools/fpgad/config_file.h | 38 + + tools/fpgad/daemonize.c | 98 ++ + tools/fpgad/event_dispatcher_thread.c | 266 ++++ + tools/fpgad/event_dispatcher_thread.h | 63 + + tools/fpgad/events_api_thread.c | 296 +++++ + tools/fpgad/events_api_thread.h | 43 + + tools/fpgad/fpgad.c | 192 +++ + tools/fpgad/fpgad.cfg | 38 + + tools/fpgad/fpgad.conf | 4 + + tools/fpgad/fpgad.h | 76 ++ + tools/fpgad/fpgad.service.in | 21 + + tools/fpgad/monitor_thread.c | 261 ++++ + tools/fpgad/monitor_thread.h | 50 + + tools/fpgad/monitored_device.c | 394 ++++++ + tools/fpgad/monitored_device.h | 123 ++ + tools/fpgad/plugins/fpgad-vc/CMakeLists.txt | 41 + + tools/fpgad/plugins/fpgad-vc/fpgad-vc.c | 1085 +++++++++++++++++ + .../fpgad/plugins/fpgad-xfpga/CMakeLists.txt | 41 + + tools/fpgad/plugins/fpgad-xfpga/fpgad-xfpga.c | 992 +++++++++++++++ + 33 files changed, 6205 insertions(+) + create mode 100644 tools/fpgad/CMakeLists.txt + create mode 100644 tools/fpgad/api/CMakeLists.txt + create mode 100644 tools/fpgad/api/device_monitoring.c + create mode 100644 tools/fpgad/api/device_monitoring.h + create mode 100644 tools/fpgad/api/logging.c + create mode 100644 tools/fpgad/api/logging.h + create mode 100644 tools/fpgad/api/opae_events_api.c + create mode 100644 tools/fpgad/api/opae_events_api.h + create mode 100644 tools/fpgad/api/sysfs.c + create mode 100644 tools/fpgad/api/sysfs.h + create mode 100644 tools/fpgad/command_line.c + create mode 100644 tools/fpgad/command_line.h + create mode 100644 tools/fpgad/config_file.c + create mode 100644 tools/fpgad/config_file.h + create mode 100644 tools/fpgad/daemonize.c + create mode 100644 tools/fpgad/event_dispatcher_thread.c + create mode 100644 tools/fpgad/event_dispatcher_thread.h + create mode 100644 tools/fpgad/events_api_thread.c + create mode 100644 tools/fpgad/events_api_thread.h + create mode 100644 tools/fpgad/fpgad.c + create mode 100644 tools/fpgad/fpgad.cfg + create mode 100644 tools/fpgad/fpgad.conf + create mode 100644 tools/fpgad/fpgad.h + create mode 100644 tools/fpgad/fpgad.service.in + create mode 100644 tools/fpgad/monitor_thread.c + create mode 100644 tools/fpgad/monitor_thread.h + create mode 100644 tools/fpgad/monitored_device.c + create mode 100644 tools/fpgad/monitored_device.h + create mode 100644 tools/fpgad/plugins/fpgad-vc/CMakeLists.txt + create mode 100644 tools/fpgad/plugins/fpgad-vc/fpgad-vc.c + create mode 100644 tools/fpgad/plugins/fpgad-xfpga/CMakeLists.txt + create mode 100644 tools/fpgad/plugins/fpgad-xfpga/fpgad-xfpga.c + +diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt +index d4b55036..a6a0bc9b 100644 +--- a/tools/CMakeLists.txt ++++ b/tools/CMakeLists.txt +@@ -32,6 +32,7 @@ opae_add_subdirectory(fpgaconf) + opae_add_subdirectory(fpgainfo) + opae_add_subdirectory(fpgametrics) + opae_add_subdirectory(fpgaport) ++opae_add_subdirectory(fpgad) + + # extra + opae_add_subdirectory(extra/userclk) +diff --git a/tools/fpgad/CMakeLists.txt b/tools/fpgad/CMakeLists.txt +new file mode 100644 +index 00000000..19d1e7c5 +--- /dev/null ++++ b/tools/fpgad/CMakeLists.txt +@@ -0,0 +1,82 @@ ++## Copyright(c) 2017-2020, Intel Corporation ++## ++## Redistribution and use in source and binary forms, with or without ++## modification, are permitted provided that the following conditions are met: ++## ++## * Redistributions of source code must retain the above copyright notice, ++## this list of conditions and the following disclaimer. ++## * Redistributions in binary form must reproduce the above copyright notice, ++## this list of conditions and the following disclaimer in the documentation ++## and/or other materials provided with the distribution. ++## * Neither the name of Intel Corporation nor the names of its contributors ++## may be used to endorse or promote products derived from this software ++## without specific prior written permission. ++## ++## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++## POSSIBILITY OF SUCH DAMAGE. ++ ++add_subdirectory(api) ++add_subdirectory(plugins/fpgad-xfpga) ++add_subdirectory(plugins/fpgad-vc) ++ ++opae_add_executable(TARGET fpgad ++ SOURCE ++ command_line.c ++ config_file.c ++ event_dispatcher_thread.c ++ events_api_thread.c ++ fpgad.c ++ monitor_thread.c ++ daemonize.c ++ monitored_device.c ++ LIBS ++ opae-c ++ bitstream ++ fpgad-api ++ dl ++ rt ++ ${libjson-c_LIBRARIES} ++ ${libuuid_LIBRARIES} ++ COMPONENT toolfpgad ++) ++ ++target_include_directories(fpgad ++ PRIVATE ++ ${OPAE_LIBS_ROOT}/libopae-c ++ ${OPAE_LIBS_ROOT}/libbitstream ++ ${OPAE_SDK_SOURCE}/tools ++) ++ ++configure_file(fpgad.service.in fpgad.service @ONLY NEWLINE_STYLE UNIX) ++ ++if ("${CPACK_GENERATOR}" STREQUAL "RPM") ++ ++ if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr") ++ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fpgad.service DESTINATION /etc/systemd/system COMPONENT toolfpgad) ++ else() ++ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fpgad.service DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/systemd/system COMPONENT toolfpgad) ++ endif() ++ ++else() ++ ++ # DEB ++ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fpgad.service DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/systemd/system COMPONENT toolfpgad) ++ ++endif() ++ ++if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr") ++ install(FILES fpgad.cfg DESTINATION /etc/opae COMPONENT toolfpgad) ++ install(FILES fpgad.conf DESTINATION /etc/sysconfig COMPONENT toolfpgad) ++else() ++ install(FILES fpgad.cfg DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/opae COMPONENT toolfpgad) ++ install(FILES fpgad.conf DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/sysconfig COMPONENT toolfpgad) ++endif() +diff --git a/tools/fpgad/api/CMakeLists.txt b/tools/fpgad/api/CMakeLists.txt +new file mode 100644 +index 00000000..cdf86c4f +--- /dev/null ++++ b/tools/fpgad/api/CMakeLists.txt +@@ -0,0 +1,46 @@ ++## Copyright(c) 2018-2020, Intel Corporation ++## ++## Redistribution and use in source and binary forms, with or without ++## modification, are permitted provided that the following conditions are met: ++## ++## * Redistributions of source code must retain the above copyright notice, ++## this list of conditions and the following disclaimer. ++## * Redistributions in binary form must reproduce the above copyright notice, ++## this list of conditions and the following disclaimer in the documentation ++## and/or other materials provided with the distribution. ++## * Neither the name of Intel Corporation nor the names of its contributors ++## may be used to endorse or promote products derived from this software ++## without specific prior written permission. ++## ++## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++## POSSIBILITY OF SUCH DAMAGE. ++ ++opae_add_shared_library(TARGET fpgad-api ++ SOURCE ++ logging.c ++ opae_events_api.c ++ device_monitoring.c ++ sysfs.c ++ LIBS ++ ${CMAKE_THREAD_LIBS_INIT} ++ ${libjson-c_LIBRARIES} ++ VERSION ${OPAE_VERSION} ++ SOVERSION ${OPAE_VERSION_MAJOR} ++ COMPONENT toolfpgad_api ++) ++ ++target_include_directories(fpgad-api ++ PRIVATE ++ ${OPAE_SDK_SOURCE}/tools ++ ${OPAE_LIBS_ROOT}/libopae-c ++ ${OPAE_LIBS_ROOT}/libbitstream ++) +diff --git a/tools/fpgad/api/device_monitoring.c b/tools/fpgad/api/device_monitoring.c +new file mode 100644 +index 00000000..b9207519 +--- /dev/null ++++ b/tools/fpgad/api/device_monitoring.c +@@ -0,0 +1,73 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include "device_monitoring.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("device_monitoring: " format, ##__VA_ARGS__) ++ ++bool mon_has_error_occurred(fpgad_monitored_device *d, void *err) ++{ ++ unsigned i; ++ for (i = 0 ; i < d->num_error_occurrences ; ++i) { ++ if (err == d->error_occurrences[i]) ++ return true; ++ } ++ return false; ++} ++ ++bool mon_add_device_error(fpgad_monitored_device *d, void *err) ++{ ++ if (d->num_error_occurrences < ++ (sizeof(d->error_occurrences) / ++ sizeof(d->error_occurrences[0]))) { ++ d->error_occurrences[d->num_error_occurrences++] = err; ++ return true; ++ } ++ LOG("exceeded max number of device errors!\n"); ++ return false; ++} ++ ++void mon_remove_device_error(fpgad_monitored_device *d, void *err) ++{ ++ unsigned i; ++ unsigned j; ++ unsigned removed = 0; ++ for (i = j = 0 ; i < d->num_error_occurrences ; ++i) { ++ if (d->error_occurrences[i] != err) ++ d->error_occurrences[j++] = d->error_occurrences[i]; ++ else ++ ++removed; ++ } ++ d->num_error_occurrences -= removed; ++} +diff --git a/tools/fpgad/api/device_monitoring.h b/tools/fpgad/api/device_monitoring.h +new file mode 100644 +index 00000000..c782665e +--- /dev/null ++++ b/tools/fpgad/api/device_monitoring.h +@@ -0,0 +1,46 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_API_DEVICE_MONITORING_H__ ++#define __FPGAD_API_DEVICE_MONITORING_H__ ++ ++#ifndef __USE_GNU ++#define __USE_GNU ++#endif ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++ ++#include "fpgad/fpgad.h" ++#include "fpgad/monitored_device.h" ++ ++bool mon_has_error_occurred(fpgad_monitored_device *d, void *err); ++ ++bool mon_add_device_error(fpgad_monitored_device *d, void *err); ++ ++void mon_remove_device_error(fpgad_monitored_device *d, void *err); ++ ++#endif /* __FPGAD_API_DEVICE_MONITORING_H__ */ +diff --git a/tools/fpgad/api/logging.c b/tools/fpgad/api/logging.c +new file mode 100644 +index 00000000..e33eeddc +--- /dev/null ++++ b/tools/fpgad/api/logging.c +@@ -0,0 +1,132 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include "logging.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("logging: " format, ##__VA_ARGS__) ++ ++STATIC pthread_mutex_t log_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; ++STATIC FILE *log_file; ++ ++#define BUF_TIME_LEN 256 ++ ++int log_open(const char *filename) ++{ ++ int res; ++ int err; ++ ++ fpgad_mutex_lock(err, &log_lock); ++ ++ log_file = fopen(filename, "a"); ++ if (log_file) { ++ time_t raw; ++ struct tm tm; ++ char timebuf[BUF_TIME_LEN]; ++ size_t len; ++ ++ time(&raw); ++ localtime_r(&raw, &tm); ++ asctime_r(&tm, timebuf); ++ ++ len = strnlen(timebuf, sizeof(timebuf)); ++ if (len < BUF_TIME_LEN) { ++ timebuf[len - 1] = '\0'; /* erase \n */ ++ } else { ++ printf(" Invalid time stamp buffer size \n"); ++ fpgad_mutex_unlock(err, &log_lock); ++ return -1; ++ } ++ ++ res = fprintf(log_file, "----- %s -----\n", timebuf); ++ fflush(log_file); ++ } else { ++ res = -1; ++ } ++ ++ fpgad_mutex_unlock(err, &log_lock); ++ ++ return res; ++} ++ ++int log_printf(const char *fmt, ...) ++{ ++ va_list l; ++ int res = -1; ++ int err; ++ ++ va_start(l, fmt); ++ ++ fpgad_mutex_lock(err, &log_lock); ++ ++ if (log_file) { ++ res = vfprintf(log_file, fmt, l); ++ fflush(log_file); ++ } ++ ++ fpgad_mutex_unlock(err, &log_lock); ++ ++ va_end(l); ++ ++ return res; ++} ++ ++void log_set(FILE *fptr) ++{ ++ int err; ++ ++ fpgad_mutex_lock(err, &log_lock); ++ ++ log_close(); ++ log_file = fptr; ++ ++ fpgad_mutex_unlock(err, &log_lock); ++} ++ ++void log_close(void) ++{ ++ int err; ++ ++ fpgad_mutex_lock(err, &log_lock); ++ ++ if (log_file) { ++ if (log_file != stdout && ++ log_file != stderr) { ++ fclose(log_file); ++ } ++ log_file = NULL; ++ } ++ ++ fpgad_mutex_unlock(err, &log_lock); ++} +diff --git a/tools/fpgad/api/logging.h b/tools/fpgad/api/logging.h +new file mode 100644 +index 00000000..b4e95c1d +--- /dev/null ++++ b/tools/fpgad/api/logging.h +@@ -0,0 +1,37 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_API_LOGGING_H__ ++#define __FPGAD_API_LOGGING_H__ ++ ++#include "fpgad/fpgad.h" ++ ++int log_open(const char *filename); ++int log_printf(const char *fmt, ...); ++void log_set(FILE *fptr); ++void log_close(void); ++ ++#endif /* __FPGAD_API_LOGGING_H__ */ +diff --git a/tools/fpgad/api/opae_events_api.c b/tools/fpgad/api/opae_events_api.c +new file mode 100644 +index 00000000..14a757f0 +--- /dev/null ++++ b/tools/fpgad/api/opae_events_api.c +@@ -0,0 +1,239 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++ ++#include "opae_events_api.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("opae_events_api: " format, ##__VA_ARGS__) ++ ++STATIC pthread_mutex_t list_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; ++STATIC api_client_event_registry *event_registry_list; ++ ++int opae_api_register_event(int conn_socket, ++ int fd, ++ fpga_event_type e, ++ uint64_t object_id) ++{ ++ api_client_event_registry *r = ++ (api_client_event_registry *) malloc(sizeof(*r)); ++ int err; ++ ++ if (!r) ++ return ENOMEM; ++ ++ r->conn_socket = conn_socket; ++ r->fd = fd; ++ r->data = 1; ++ r->event = e; ++ r->object_id = object_id; ++ ++ fpgad_mutex_lock(err, &list_lock); ++ ++ r->next = event_registry_list; ++ event_registry_list = r; ++ ++ fpgad_mutex_unlock(err, &list_lock); ++ ++ return 0; ++} ++ ++STATIC void release_event_registry(api_client_event_registry *r) ++{ ++ close(r->fd); ++ free(r); ++} ++ ++int opae_api_unregister_event(int conn_socket, ++ fpga_event_type e, ++ uint64_t object_id) ++{ ++ api_client_event_registry *trash; ++ api_client_event_registry *save; ++ int err; ++ int res = 0; ++ ++ fpgad_mutex_lock(err, &list_lock); ++ ++ trash = event_registry_list; ++ ++ if (!trash) { // empty list ++ res = 1; ++ goto out_unlock; ++ } ++ ++ if ((conn_socket == trash->conn_socket) && ++ (e == trash->event) && ++ (object_id == trash->object_id)) { ++ ++ // found at head of list ++ ++ event_registry_list = event_registry_list->next; ++ release_event_registry(trash); ++ goto out_unlock; ++ } ++ ++ save = trash; ++ trash = trash->next; ++ while (trash) { ++ ++ if ((conn_socket == trash->conn_socket) && ++ (e == trash->event) && ++ (object_id == trash->object_id)) ++ break; ++ ++ save = trash; ++ trash = trash->next; ++ } ++ ++ if (!trash) { // not found ++ res = 1; ++ goto out_unlock; ++ } ++ ++ // found at trash ++ save->next = trash->next; ++ release_event_registry(trash); ++ ++out_unlock: ++ fpgad_mutex_unlock(err, &list_lock); ++ return res; ++} ++ ++STATIC api_client_event_registry * ++find_event_for(int conn_socket) ++{ ++ api_client_event_registry *r; ++ ++ for (r = event_registry_list ; r ; r = r->next) ++ if (conn_socket == r->conn_socket) ++ break; ++ ++ return r; ++} ++ ++void opae_api_unregister_all_events_for(int conn_socket) ++{ ++ api_client_event_registry *r; ++ int err; ++ ++ fpgad_mutex_lock(err, &list_lock); ++ ++ r = find_event_for(conn_socket); ++ while (r) { ++ opae_api_unregister_event(conn_socket, r->event, r->object_id); ++ r = find_event_for(conn_socket); ++ } ++ ++ fpgad_mutex_unlock(err, &list_lock); ++} ++ ++void opae_api_unregister_all_events(void) ++{ ++ api_client_event_registry *r; ++ int err; ++ ++ fpgad_mutex_lock(err, &list_lock); ++ ++ for (r = event_registry_list ; r != NULL ; ) { ++ api_client_event_registry *trash; ++ trash = r; ++ r = r->next; ++ release_event_registry(trash); ++ } ++ ++ event_registry_list = NULL; ++ ++ fpgad_mutex_unlock(err, &list_lock); ++} ++ ++void opae_api_for_each_registered_event ++(void (*cb)(api_client_event_registry *r, void *context), ++void *context) ++{ ++ api_client_event_registry *r; ++ int err; ++ ++ fpgad_mutex_lock(err, &list_lock); ++ ++ for (r = event_registry_list; r != NULL; r = r->next) { ++ cb(r, context); ++ } ++ ++ fpgad_mutex_unlock(err, &list_lock); ++} ++ ++STATIC void check_and_send_EVENT_ERROR(api_client_event_registry *r, ++ void *context) ++{ ++ fpgad_monitored_device *d = ++ (fpgad_monitored_device *)context; ++ ++ if ((r->event == FPGA_EVENT_ERROR) && ++ (r->object_id == d->object_id)) { ++ LOG("object_id: 0x%" PRIx64 " event: FPGA_EVENT_ERROR\n", ++ d->object_id); ++ if (write(r->fd, &r->data, sizeof(r->data)) < 0) ++ LOG("write failed: %s\n", strerror(errno)); ++ r->data++; ++ } ++} ++ ++void opae_api_send_EVENT_ERROR(fpgad_monitored_device *d) ++{ ++ opae_api_for_each_registered_event(check_and_send_EVENT_ERROR, ++ d); ++} ++ ++STATIC void check_and_send_EVENT_POWER_THERMAL(api_client_event_registry *r, ++ void *context) ++{ ++ fpgad_monitored_device *d = ++ (fpgad_monitored_device *)context; ++ ++ if ((r->event == FPGA_EVENT_POWER_THERMAL) && ++ (r->object_id == d->object_id)) { ++ LOG("object_id: 0x%" PRIx64 " event: FPGA_EVENT_POWER_THERMAL\n", ++ d->object_id); ++ if (write(r->fd, &r->data, sizeof(r->data)) < 0) ++ LOG("write failed: %s\n", strerror(errno)); ++ r->data++; ++ } ++} ++ ++void opae_api_send_EVENT_POWER_THERMAL(fpgad_monitored_device *d) ++{ ++ opae_api_for_each_registered_event(check_and_send_EVENT_POWER_THERMAL, ++ d); ++} +diff --git a/tools/fpgad/api/opae_events_api.h b/tools/fpgad/api/opae_events_api.h +new file mode 100644 +index 00000000..9e165cc1 +--- /dev/null ++++ b/tools/fpgad/api/opae_events_api.h +@@ -0,0 +1,85 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_API_OPAE_EVENTS_API_H__ ++#define __FPGAD_API_OPAE_EVENTS_API_H__ ++ ++#ifndef __USE_GNU ++#define __USE_GNU ++#endif ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++ ++#include ++ ++#include "fpgad/fpgad.h" ++#include "fpgad/monitored_device.h" ++ ++enum request_type { ++ REGISTER_EVENT = 0, ++ UNREGISTER_EVENT ++}; ++ ++struct event_request { ++ enum request_type type; ++ fpga_event_type event; ++ uint64_t object_id; ++}; ++ ++typedef struct _api_client_event_registry { ++ int conn_socket; ++ int fd; ++ uint64_t data; ++ fpga_event_type event; ++ uint64_t object_id; ++ struct _api_client_event_registry *next; ++} api_client_event_registry; ++ ++// 0 on success ++int opae_api_register_event(int conn_socket, ++ int fd, ++ fpga_event_type e, ++ uint64_t object_id); ++ ++// 0 on success ++int opae_api_unregister_event(int conn_socket, ++ fpga_event_type e, ++ uint64_t object_id); ++ ++void opae_api_unregister_all_events_for(int conn_socket); ++ ++void opae_api_unregister_all_events(void); ++ ++void opae_api_for_each_registered_event(void (*cb)(api_client_event_registry *r, ++ void *context), ++ void *context); ++ ++void opae_api_send_EVENT_ERROR(fpgad_monitored_device *d); ++ ++void opae_api_send_EVENT_POWER_THERMAL(fpgad_monitored_device *d); ++ ++#endif /* __FPGAD_API_OPAE_EVENTS_API_H__ */ +diff --git a/tools/fpgad/api/sysfs.c b/tools/fpgad/api/sysfs.c +new file mode 100644 +index 00000000..ae8d1403 +--- /dev/null ++++ b/tools/fpgad/api/sysfs.c +@@ -0,0 +1,76 @@ ++// Copyright(c) 2019-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++ ++#include "logging.h" ++#include "sysfs.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("sysfs: " format, ##__VA_ARGS__) ++ ++int file_write_string(const char *path, const char *str, size_t len) ++{ ++ FILE *fp; ++ size_t num; ++ ++ fp = fopen(path, "w"); ++ if (!fp) ++ return 1; ++ ++ num = fwrite(str, 1, len, fp); ++ ++ if (!num || ferror(fp)) { ++ fclose(fp); ++ return 1; ++ } ++ ++ fclose(fp); ++ ++ return 0; ++} ++ ++char *cstr_dup(const char *s) ++{ ++ char *p; ++ size_t len = strnlen(s, 8192); ++ ++ p = malloc(len+1); ++ if (!p) ++ return NULL; ++ ++ memcpy(p, s, len); ++ p[len] = '\0'; ++ ++ return p; ++} +diff --git a/tools/fpgad/api/sysfs.h b/tools/fpgad/api/sysfs.h +new file mode 100644 +index 00000000..f8f3b3d9 +--- /dev/null ++++ b/tools/fpgad/api/sysfs.h +@@ -0,0 +1,45 @@ ++// Copyright(c) 2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_API_SYSFS_H__ ++#define __FPGAD_API_SYSFS_H__ ++ ++#ifndef __USE_GNU ++#define __USE_GNU ++#endif ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++ ++#include ++#include ++ ++// 0 on success ++int file_write_string(const char *path, const char *str, size_t len); ++ ++char *cstr_dup(const char *s); ++ ++#endif /* __FPGAD_API_SYSFS_H__ */ +diff --git a/tools/fpgad/command_line.c b/tools/fpgad/command_line.c +new file mode 100644 +index 00000000..4e0bfb9d +--- /dev/null ++++ b/tools/fpgad/command_line.c +@@ -0,0 +1,516 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#define _GNU_SOURCE ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include ++#include ++#include ++#include ++#include "command_line.h" ++#include "config_file.h" ++#include "monitored_device.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("args: " format, ##__VA_ARGS__) ++ ++extern fpgad_supported_device default_supported_devices_table[]; ++ ++#define OPT_STR ":hdl:p:s:n:c:v" ++ ++STATIC struct option longopts[] = { ++ { "help", no_argument, NULL, 'h' }, ++ { "daemon", no_argument, NULL, 'd' }, ++ { "logfile", required_argument, NULL, 'l' }, ++ { "pidfile", required_argument, NULL, 'p' }, ++ { "socket", required_argument, NULL, 's' }, ++ { "null-bitstream", required_argument, NULL, 'n' }, ++ { "config", required_argument, NULL, 'c' }, ++ { "version", no_argument, NULL, 'v' }, ++ ++ { 0, 0, 0, 0 } ++}; ++ ++#define DEFAULT_DIR_ROOT "/var/lib/opae" ++#define DEFAULT_DIR_ROOT_SIZE 13 ++#define DEFAULT_LOG "fpgad.log" ++#define DEFAULT_PID "fpgad.pid" ++#define DEFAULT_CFG "fpgad.cfg" ++ ++void cmd_show_help(FILE *fptr) ++{ ++ fprintf(fptr, "Usage: fpgad \n"); ++ fprintf(fptr, "\n"); ++ fprintf(fptr, "\t-d,--daemon run as daemon process.\n"); ++ fprintf(fptr, "\t-l,--logfile the log file for daemon mode [%s].\n", DEFAULT_LOG); ++ fprintf(fptr, "\t-p,--pidfile the pid file for daemon mode [%s].\n", DEFAULT_PID); ++ fprintf(fptr, "\t-s,--socket the unix domain socket [/tmp/fpga_event_socket].\n"); ++ fprintf(fptr, "\t-n,--null-bitstream NULL bitstream (for AP6 handling, may be\n" ++ "\t given multiple times).\n"); ++ fprintf(fptr, "\t-c,--config the configuration file [%s].\n", DEFAULT_CFG); ++ fprintf(fptr, "\t-v,--version display the version and exit.\n"); ++} ++ ++STATIC bool cmd_register_null_gbs(struct fpgad_config *c, char *null_gbs_path) ++{ ++ char *canon_path = NULL; ++ ++ if (c->num_null_gbs < (sizeof(c->null_gbs) / sizeof(c->null_gbs[0]))) { ++ canon_path = canonicalize_file_name(null_gbs_path); ++ ++ if (canon_path) { ++ ++ memset(&c->null_gbs[c->num_null_gbs], 0, ++ sizeof(opae_bitstream_info)); ++ ++ if (opae_load_bitstream(canon_path, ++ &c->null_gbs[c->num_null_gbs])) { ++ LOG("failed to load NULL GBS \"%s\"\n", canon_path); ++ opae_unload_bitstream(&c->null_gbs[c->num_null_gbs]); ++ free(canon_path); ++ return false; ++ } ++ ++ c->num_null_gbs++; ++ ++ LOG("registering NULL bitstream \"%s\"\n", canon_path); ++ ++ } else { ++ LOG("error with NULL GBS argument: %s\n", strerror(errno)); ++ return false; ++ } ++ ++ } else { ++ LOG("maximum number of NULL bitstreams exceeded. Ignoring -n option.\n"); ++ } ++ return true; ++} ++ ++int cmd_parse_args(struct fpgad_config *c, int argc, char *argv[]) ++{ ++ int getopt_ret; ++ int option_index; ++ size_t len; ++ ++ while (-1 != (getopt_ret = getopt_long(argc, argv, OPT_STR, longopts, &option_index))) { ++ const char *tmp_optarg = optarg; ++ ++ if (optarg && ('=' == *tmp_optarg)) ++ ++tmp_optarg; ++ ++ if (!optarg && (optind < argc) && ++ (NULL != argv[optind]) && ++ ('-' != argv[optind][0])) ++ tmp_optarg = argv[optind++]; ++ ++ switch (getopt_ret) { ++ case 'h': ++ cmd_show_help(stdout); ++ return -2; ++ break; ++ ++ case 'd': ++ c->daemon = 1; ++ LOG("daemon requested\n"); ++ break; ++ ++ case 'l': ++ if (tmp_optarg) { ++ len = strnlen(tmp_optarg, PATH_MAX - 1); ++ memcpy(c->logfile, tmp_optarg, len); ++ c->logfile[len] = '\0'; ++ } else { ++ LOG("missing logfile parameter.\n"); ++ return 1; ++ } ++ break; ++ ++ case 'p': ++ if (tmp_optarg) { ++ len = strnlen(tmp_optarg, PATH_MAX - 1); ++ memcpy(c->pidfile, tmp_optarg, len); ++ c->pidfile[len] = '\0'; ++ } else { ++ LOG("missing pidfile parameter.\n"); ++ return 1; ++ } ++ break; ++ ++ case 'n': ++ if (tmp_optarg) { ++ if (!cmd_register_null_gbs(c, (char *)tmp_optarg)) { ++ LOG("invalid null gbs path: \"%s\"\n", tmp_optarg); ++ return 1; ++ } ++ } else { ++ LOG("missing bitstream parameter.\n"); ++ return 1; ++ } ++ break; ++ ++ case 's': ++ if (tmp_optarg) { ++ c->api_socket = tmp_optarg; ++ LOG("daemon socket is %s\n", c->api_socket); ++ } else { ++ LOG("missing socket parameter.\n"); ++ return 1; ++ } ++ break; ++ ++ case 'c': ++ if (tmp_optarg) { ++ len = strnlen(tmp_optarg, PATH_MAX - 1); ++ memcpy(c->cfgfile, tmp_optarg, len); ++ c->cfgfile[len] = '\0'; ++ } else { ++ LOG("missing cfgfile parameter.\n"); ++ return 1; ++ } ++ break; ++ ++ case 'v': ++ fprintf(stdout, "fpgad %s %s%s\n", ++ OPAE_VERSION, ++ OPAE_GIT_COMMIT_HASH, ++ OPAE_GIT_SRC_TREE_DIRTY ? "*":""); ++ return -2; ++ break; ++ ++ case ':': ++ LOG("Missing option argument.\n"); ++ return 1; ++ ++ case '?': ++ LOG("Invalid command option.\n"); ++ return 1; ++ ++ default: ++ LOG("Invalid command option.\n"); ++ return 1; ++ } ++ ++ } ++ ++ return 0; ++} ++ ++int cmd_canonicalize_paths(struct fpgad_config *c) ++{ ++ char *sub; ++ bool def; ++ mode_t mode; ++ struct stat stat_buf; ++ bool search = true; ++ char buf[PATH_MAX]; ++ char *canon_path; ++ uid_t uid; ++ size_t len; ++ ++ uid = geteuid(); ++ ++ if (!uid) { ++ // If we're being run as root, then use DEFAULT_DIR_ROOT ++ // as the working directory. ++ memcpy(c->directory, DEFAULT_DIR_ROOT, sizeof(DEFAULT_DIR_ROOT)); ++ c->directory[sizeof(DEFAULT_DIR_ROOT)] = '\0'; ++ mode = 0755; ++ c->filemode = 0026; ++ } else { ++ // We're not root. Try to use ${HOME}/.opae ++ struct passwd *passwd; ++ ++ passwd = getpwuid(uid); ++ ++ canon_path = canonicalize_file_name(passwd->pw_dir); ++ ++ if (canon_path) { ++ snprintf(c->directory, sizeof(c->directory), ++ "%s/.opae", canon_path); ++ free(canon_path); ++ } else { ++ // ${HOME} not found or invalid - use current dir. ++ if (getcwd(buf, sizeof(buf))) { ++ if (snprintf(c->directory, sizeof(c->directory), ++ "%s/.opae", buf) < 0) { ++ len = strnlen("./.opae", ++ sizeof(c->directory) - 1); ++ memcpy(c->directory, "./.opae", len); ++ c->directory[len] = '\0'; ++ } ++ } else { ++ // Current directory not found - use / ++ len = strnlen("/.opae", sizeof(c->directory) - 1); ++ memcpy(c->directory, "/.opae", len); ++ c->directory[len] = '\0'; ++ } ++ } ++ ++ mode = 0775; ++ c->filemode = 0022; ++ } ++ ++ if (cmd_path_is_symlink(c->directory)) { ++ LOG("Aborting - working directory contains a link: %s\n.", ++ c->directory); ++ return 1; ++ } ++ LOG("daemon working directory is %s\n", c->directory); ++ ++ // Create the directory if it doesn't exist. ++ if (lstat(c->directory, &stat_buf) && (errno == ENOENT)) { ++ if (mkdir(c->directory, mode)) { ++ LOG("mkdir failed\n"); ++ return 1; ++ } ++ } ++ ++ // Verify logfile and pidfile do not contain ".." ++ // nor "/". ++ def = false; ++ sub = strstr(c->logfile, ".."); ++ if (sub) ++ def = true; ++ ++ sub = strstr(c->logfile, "/"); ++ if (sub) ++ def = true; ++ ++ if (def || (c->logfile[0] == '\0')) { ++ if (snprintf(c->logfile, sizeof(c->logfile), ++ "%s/%s", c->directory, DEFAULT_LOG) < 0) { ++ len = strnlen("./" DEFAULT_LOG, ++ sizeof(c->logfile) - 1); ++ memcpy(c->logfile, "./" DEFAULT_LOG, len); ++ c->logfile[len] = '\0'; ++ } ++ } else { ++ len = strnlen(c->logfile, sizeof(buf) - 1); ++ memcpy(buf, c->logfile, len); ++ buf[len] = '\0'; ++ ++ if (snprintf(c->logfile, sizeof(c->logfile), ++ "%s/%s", c->directory, buf) < 0) { ++ len = strnlen("./" DEFAULT_LOG, ++ sizeof(c->logfile) - 1); ++ memcpy(c->logfile, "./" DEFAULT_LOG, len); ++ c->logfile[len] = '\0'; ++ } ++ } ++ ++ if (cmd_path_is_symlink(c->logfile)) { ++ LOG("Aborting - log file path contains a link: %s\n.", ++ c->logfile); ++ return 1; ++ } ++ LOG("daemon log file is %s\n", c->logfile); ++ ++ def = false; ++ sub = strstr(c->pidfile, ".."); ++ if (sub) ++ def = true; ++ ++ sub = strstr(c->pidfile, "/"); ++ if (sub) ++ def = true; ++ ++ if (def || (c->pidfile[0] == '\0')) { ++ ++ if (snprintf(c->pidfile, sizeof(c->pidfile), ++ "%s/%s", c->directory, DEFAULT_PID) < 0) { ++ len = strnlen("./" DEFAULT_PID, ++ sizeof(c->pidfile) - 1); ++ memcpy(c->pidfile, "./" DEFAULT_PID, len); ++ c->pidfile[len] = '\0'; ++ } ++ ++ } else { ++ len = strnlen(c->pidfile, sizeof(buf) - 1); ++ memcpy(buf, c->pidfile, len); ++ buf[len] = '\0'; ++ ++ if (snprintf(c->pidfile, sizeof(c->pidfile), ++ "%s/%s", c->directory, buf) < 0) { ++ len = strnlen("./" DEFAULT_PID, ++ sizeof(c->pidfile) - 1); ++ memcpy(c->pidfile, "./" DEFAULT_PID, len); ++ c->pidfile[len] = '\0'; ++ } ++ } ++ ++ if (cmd_path_is_symlink(c->pidfile)) { ++ LOG("Aborting - pid file path contains a link: %s\n.", ++ c->pidfile); ++ return 1; ++ } ++ LOG("daemon pid file is %s\n", c->pidfile); ++ ++ // Verify cfgfile doesn't contain ".." ++ def = false; ++ sub = strstr(c->cfgfile, ".."); ++ if (sub) ++ def = true; ++ ++ if (def || (c->cfgfile[0] == '\0')) { ++ search = true; ++ } else { ++ canon_path = canonicalize_file_name(c->cfgfile); ++ if (canon_path) { ++ ++ if (!cmd_path_is_symlink(c->cfgfile)) { ++ ++ len = strnlen(canon_path, ++ sizeof(c->cfgfile) - 1); ++ memcpy(c->cfgfile, ++ canon_path, ++ len); ++ c->cfgfile[len] = '\0'; ++ ++ if (!cfg_load_config(c)) { ++ LOG("daemon cfg file is %s\n", ++ c->cfgfile); ++ search = false; // found and loaded it ++ } ++ ++ } ++ ++ free(canon_path); ++ } ++ } ++ ++ if (search) { ++ c->cfgfile[0] = '\0'; ++ if (cfg_find_config_file(c)) ++ LOG("failed to find config file.\n"); ++ else { ++ if (cfg_load_config(c)) ++ LOG("failed to load config file %s\n", ++ c->cfgfile); ++ else ++ LOG("daemon cfg file is %s\n", c->cfgfile); ++ } ++ } ++ ++ if (!c->supported_devices) { ++ LOG("using default configuration.\n"); ++ c->cfgfile[0] = '\0'; ++ c->supported_devices = default_supported_devices_table; ++ } ++ ++ return 0; ++} ++ ++void cmd_destroy(struct fpgad_config *c) ++{ ++ unsigned i; ++ ++ if (c->daemon) ++ unlink(c->pidfile); ++ ++ for (i = 0 ; i < c->num_null_gbs ; ++i) { ++ if (c->null_gbs[i].filename) ++ free((char *)c->null_gbs[i].filename); ++ opae_unload_bitstream(&c->null_gbs[i]); ++ } ++ c->num_null_gbs = 0; ++ ++ if (c->supported_devices && ++ (c->supported_devices != default_supported_devices_table)) { ++ ++ for (i = 0 ; c->supported_devices[i].library_path ; ++i) { ++ fpgad_supported_device *d = &c->supported_devices[i]; ++ if (d->library_path) ++ free((void *)d->library_path); ++ if (d->config) ++ free((void *)d->config); ++ } ++ ++ free(c->supported_devices); ++ } ++ c->supported_devices = NULL; ++} ++ ++bool cmd_path_is_symlink(const char *path) ++{ ++ char component[PATH_MAX]; ++ struct stat stat_buf; ++ size_t len; ++ char *pslash; ++ ++ len = strnlen(path, PATH_MAX - 1); ++ if (!len) // empty path ++ return false; ++ ++ memcpy(component, path, len); ++ component[len] = '\0'; ++ ++ if (component[0] == '/') { ++ // absolute path ++ ++ pslash = realpath(path, component); ++ ++ if (strcmp(component, path)) ++ return true; ++ ++ ++ } else { ++ // relative path ++ ++ pslash = strrchr(component, '/'); ++ ++ while (pslash) { ++ ++ if (fstatat(AT_FDCWD, component, ++ &stat_buf, AT_SYMLINK_NOFOLLOW)) { ++ return false; ++ } ++ ++ if (S_ISLNK(stat_buf.st_mode)) ++ return true; ++ ++ *pslash = '\0'; ++ pslash = strrchr(component, '/'); ++ } ++ ++ if (fstatat(AT_FDCWD, component, ++ &stat_buf, AT_SYMLINK_NOFOLLOW)) { ++ return false; ++ } ++ ++ if (S_ISLNK(stat_buf.st_mode)) ++ return true; ++ ++ } ++ ++ return false; ++} +diff --git a/tools/fpgad/command_line.h b/tools/fpgad/command_line.h +new file mode 100644 +index 00000000..99aec655 +--- /dev/null ++++ b/tools/fpgad/command_line.h +@@ -0,0 +1,84 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_COMMAND_LINE_H__ ++#define __FPGAD_COMMAND_LINE_H__ ++ ++#include ++#include ++#include ++#include ++#include "bitstream.h" ++ ++struct _fpgad_supported_device; ++ ++#define MAX_NULL_GBS 32 ++ ++struct fpgad_config { ++ useconds_t poll_interval_usec; ++ ++ bool daemon; ++ char directory[PATH_MAX]; ++ char logfile[PATH_MAX]; ++ char pidfile[PATH_MAX]; ++ char cfgfile[PATH_MAX]; ++ mode_t filemode; ++ ++ bool running; ++ ++ const char *api_socket; ++ ++ opae_bitstream_info null_gbs[MAX_NULL_GBS]; ++ unsigned num_null_gbs; ++ ++ pthread_t bmc_monitor_thr; ++ pthread_t monitor_thr; ++ pthread_t event_dispatcher_thr; ++ pthread_t events_api_thr; ++ ++ struct _fpgad_supported_device *supported_devices; ++}; ++ ++extern struct fpgad_config global_config; ++ ++/* ++** Returns ++** -2 if --help requested ++** -1 on parse error ++** 0 on success ++*/ ++int cmd_parse_args(struct fpgad_config *c, int argc, char *argv[]); ++ ++void cmd_show_help(FILE *fptr); ++ ++// 0 on success ++int cmd_canonicalize_paths(struct fpgad_config *c); ++ ++void cmd_destroy(struct fpgad_config *c); ++ ++bool cmd_path_is_symlink(const char *path); ++ ++#endif /* __FPGAD_COMMAND_LINE_H__ */ +diff --git a/tools/fpgad/config_file.c b/tools/fpgad/config_file.c +new file mode 100644 +index 00000000..2172d2fc +--- /dev/null ++++ b/tools/fpgad/config_file.c +@@ -0,0 +1,621 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include ++#include "config_file.h" ++#include "monitored_device.h" ++#include "api/sysfs.h" ++ ++#include ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("cfg: " format, ##__VA_ARGS__) ++ ++#define CFG_TRY_FILE(__f) \ ++do { \ ++ canon = canonicalize_file_name(__f); \ ++ if (canon) { \ ++ \ ++ if (!cmd_path_is_symlink(__f)) { \ ++ size_t len = strnlen(canon, \ ++ sizeof(c->cfgfile) - 1); \ ++ memcpy(c->cfgfile, \ ++ canon, \ ++ len); \ ++ c->cfgfile[len] = '\0'; \ ++ free(canon); \ ++ return 0; \ ++ } \ ++ \ ++ free(canon); \ ++ } \ ++} while (0) ++ ++int cfg_find_config_file(struct fpgad_config *c) ++{ ++ char path[PATH_MAX]; ++ char *e; ++ char *canon = NULL; ++ uid_t uid; ++ size_t len; ++ ++ uid = geteuid(); ++ ++ e = getenv("FPGAD_CONFIG_FILE"); ++ if (e) { ++ // try $FPGAD_CONFIG_FILE ++ len = strnlen(e, sizeof(path) - 1); ++ memcpy(path, e, len); ++ path[len] = '\0'; ++ ++ CFG_TRY_FILE(path); ++ } ++ ++ if (!uid) { ++ CFG_TRY_FILE("/var/lib/opae/fpgad.cfg"); ++ } else { ++ struct passwd *passwd; ++ ++ passwd = getpwuid(uid); ++ ++ // try $HOME/.opae/fpgad.cfg ++ snprintf(path, sizeof(path), ++ "%s/.opae/fpgad.cfg", passwd->pw_dir); ++ ++ CFG_TRY_FILE(path); ++ } ++ ++ CFG_TRY_FILE("/etc/opae/fpgad.cfg"); ++ ++ return 1; // not found ++} ++ ++STATIC char *cfg_read_file(const char *file) ++{ ++ FILE *fp; ++ size_t len; ++ char *buf; ++ ++ fp = fopen(file, "r"); ++ if (!fp) { ++ LOG("fopen failed.\n"); ++ return NULL; ++ } ++ ++ if (fseek(fp, 0, SEEK_END)) { ++ LOG("fseek failed.\n"); ++ fclose(fp); ++ return NULL; ++ } ++ ++ len = (size_t)ftell(fp); ++ ++len; // for \0 ++ ++ if (len == 1) { ++ LOG("%s is empty.\n", file); ++ fclose(fp); ++ return NULL; ++ } ++ ++ if (fseek(fp, 0, SEEK_SET)) { ++ LOG("fseek failed.\n"); ++ fclose(fp); ++ return NULL; ++ } ++ ++ buf = (char *)malloc(len); ++ if (!buf) { ++ LOG("malloc failed.\n"); ++ fclose(fp); ++ return NULL; ++ } ++ ++ if ((fread(buf, 1, len - 1, fp) != len - 1) || ++ ferror(fp)) { ++ LOG("fread failed.\n"); ++ fclose(fp); ++ free(buf); ++ return NULL; ++ } ++ ++ fclose(fp); ++ buf[len - 1] = '\0'; ++ ++ return buf; ++} ++ ++typedef struct _cfg_vendor_device_id { ++ uint16_t vendor_id; ++ uint16_t device_id; ++ struct _cfg_vendor_device_id *next; ++} cfg_vendor_device_id; ++ ++typedef struct _cfg_plugin_configuration { ++ char *configuration; ++ bool enabled; ++ char *library; ++ cfg_vendor_device_id *devices; ++ struct _cfg_plugin_configuration *next; ++} cfg_plugin_configuration; ++ ++STATIC cfg_vendor_device_id *alloc_device(uint16_t vendor_id, ++ uint16_t device_id) ++{ ++ cfg_vendor_device_id *p; ++ ++ p = (cfg_vendor_device_id *)malloc(sizeof(cfg_vendor_device_id)); ++ if (p) { ++ p->vendor_id = vendor_id; ++ p->device_id = device_id; ++ p->next = NULL; ++ } ++ ++ return p; ++} ++ ++STATIC cfg_plugin_configuration *alloc_configuration(char *configuration, ++ bool enabled, ++ char *library, ++ cfg_vendor_device_id *devs) ++{ ++ cfg_plugin_configuration *p; ++ ++ p = (cfg_plugin_configuration *) ++ malloc(sizeof(cfg_plugin_configuration)); ++ if (p) { ++ p->configuration = configuration; ++ p->enabled = enabled; ++ p->library = library; ++ p->devices = devs; ++ p->next = NULL; ++ } ++ ++ return p; ++} ++ ++STATIC cfg_vendor_device_id * ++cfg_process_plugin_devices(const char *name, ++ json_object *j_devices) ++{ ++ int i; ++ int devs; ++ cfg_vendor_device_id *head = NULL; ++ cfg_vendor_device_id *id = NULL; ++ uint16_t vendor_id; ++ uint16_t device_id; ++ const char *s; ++ char *endptr; ++ ++ if (!json_object_is_type(j_devices, json_type_array)) { ++ LOG("'devices' JSON object not array.\n"); ++ return NULL; ++ } ++ ++ devs = json_object_array_length(j_devices); ++ for (i = 0 ; i < devs ; ++i) { ++ json_object *j_dev = json_object_array_get_idx(j_devices, i); ++ json_object *j_vid; ++ json_object *j_did; ++ ++ if (!json_object_is_type(j_dev, json_type_array)) { ++ LOG("%s 'devices' entry %d not array.\n", ++ name, i); ++ goto out_free; ++ } ++ ++ if (json_object_array_length(j_dev) != 2) { ++ LOG("%s 'devices' entry %d not array[2].\n", ++ name, i); ++ goto out_free; ++ } ++ ++ j_vid = json_object_array_get_idx(j_dev, 0); ++ if (json_object_is_type(j_vid, json_type_string)) { ++ s = json_object_get_string(j_vid); ++ endptr = NULL; ++ ++ vendor_id = (uint16_t)strtoul(s, &endptr, 0); ++ if (*endptr != '\0') { ++ LOG("%s malformed Vendor ID at devices[%d]\n", ++ name, i); ++ goto out_free; ++ } ++ ++ } else if (json_object_is_type(j_vid, json_type_int)) { ++ vendor_id = (uint16_t)json_object_get_int(j_vid); ++ } else { ++ LOG("%s invalid Vendor ID at devices[%d]\n", ++ name, i); ++ goto out_free; ++ } ++ ++ j_did = json_object_array_get_idx(j_dev, 1); ++ if (json_object_is_type(j_did, json_type_string)) { ++ s = json_object_get_string(j_did); ++ endptr = NULL; ++ ++ device_id = (uint16_t)strtoul(s, &endptr, 0); ++ if (*endptr != '\0') { ++ LOG("%s malformed Device ID at devices[%d]\n", ++ name, i); ++ goto out_free; ++ } ++ ++ } else if (json_object_is_type(j_did, json_type_int)) { ++ device_id = (uint16_t)json_object_get_int(j_did); ++ } else { ++ LOG("%s invalid Device ID at devices[%d]\n", ++ name, i); ++ goto out_free; ++ } ++ ++ if (!head) { ++ head = alloc_device(vendor_id, device_id); ++ if (!head) { ++ LOG("malloc failed.\n"); ++ goto out_free; ++ } ++ ++ id = head; ++ } else { ++ id->next = alloc_device(vendor_id, device_id); ++ if (!id->next) { ++ LOG("malloc failed.\n"); ++ goto out_free; ++ } ++ ++ id = id->next; ++ } ++ } ++ ++ return head; ++ ++out_free: ++ for (id = head ; id ; ) { ++ cfg_vendor_device_id *trash = id; ++ id = id->next; ++ free(trash); ++ } ++ return NULL; ++} ++ ++STATIC int cfg_process_plugin(const char *name, ++ json_object *j_configurations, ++ cfg_plugin_configuration **list) ++{ ++ json_object *j_cfg_plugin = NULL; ++ json_object *j_cfg_plugin_configuration = NULL; ++ json_object *j_enabled = NULL; ++ json_object *j_plugin = NULL; ++ json_object *j_devices = NULL; ++ char *configuration = NULL; ++ bool enabled = false; ++ char *plugin = NULL; ++ cfg_plugin_configuration *c = NULL; ++ ++ if (!json_object_object_get_ex(j_configurations, ++ name, ++ &j_cfg_plugin)) { ++ LOG("couldn't find configurations section" ++ " for %s.\n", name); ++ return 1; ++ } ++ ++ if (!json_object_object_get_ex(j_cfg_plugin, ++ "configuration", ++ &j_cfg_plugin_configuration)) { ++ LOG("couldn't find %s configuration section.\n", name); ++ return 1; ++ } ++ ++ configuration = (char *)json_object_to_json_string_ext( ++ j_cfg_plugin_configuration, ++ JSON_C_TO_STRING_PLAIN); ++ if (!configuration) { ++ LOG("failed to parse configuration for %s.\n", name); ++ return 1; ++ } ++ ++ configuration = cstr_dup(configuration); ++ if (!configuration) { ++ LOG("cstr_dup failed.\n"); ++ return 1; ++ } ++ ++ if (!json_object_object_get_ex(j_cfg_plugin, ++ "enabled", ++ &j_enabled)) { ++ LOG("couldn't find enabled key" ++ " for %s.\n", name); ++ goto out_free; ++ } ++ ++ if (!json_object_is_type(j_enabled, json_type_boolean)) { ++ LOG("enabled key for %s not boolean.\n", name); ++ goto out_free; ++ } ++ ++ enabled = json_object_get_boolean(j_enabled); ++ ++ if (!json_object_object_get_ex(j_cfg_plugin, ++ "plugin", ++ &j_plugin)) { ++ LOG("couldn't find plugin key" ++ " for %s.\n", name); ++ goto out_free; ++ } ++ ++ if (!json_object_is_type(j_plugin, json_type_string)) { ++ LOG("plugin key for %s not string.\n", name); ++ goto out_free; ++ } ++ ++ plugin = cstr_dup(json_object_get_string(j_plugin)); ++ if (!plugin) { ++ LOG("cstr_dup failed.\n"); ++ goto out_free; ++ } ++ ++ if (!json_object_object_get_ex(j_cfg_plugin, ++ "devices", ++ &j_devices)) { ++ LOG("couldn't find devices key" ++ " for %s.\n", name); ++ goto out_free; ++ } ++ ++ if (!(*list)) { // list is empty ++ c = alloc_configuration(configuration, ++ enabled, ++ plugin, ++ NULL); ++ if (!c) { ++ LOG("malloc failed.\n"); ++ goto out_free; ++ } ++ ++ *list = c; ++ } else { ++ for (c = *list ; c->next ; c = c->next) ++ /* find the end of the list */ ; ++ ++ c->next = alloc_configuration(configuration, ++ enabled, ++ plugin, ++ NULL); ++ if (!c->next) { ++ LOG("malloc failed.\n"); ++ goto out_free; ++ } ++ ++ c = c->next; ++ } ++ ++ c->devices = cfg_process_plugin_devices(name, j_devices); ++ ++ return 0; ++ ++out_free: ++ if (configuration) ++ free(configuration); ++ if (plugin) ++ free(plugin); ++ if (c) ++ free(c); ++ return 1; ++} ++ ++STATIC fpgad_supported_device * ++cfg_json_to_supported(cfg_plugin_configuration *configurations) ++{ ++ cfg_plugin_configuration *c; ++ cfg_vendor_device_id *d; ++ size_t num_devices = 0; ++ fpgad_supported_device *supported; ++ int i; ++ ++ // find the number of devices ++ for (c = configurations ; c ; c = c->next) { ++ if (!c->enabled) // skip it ++ continue; ++ for (d = c->devices ; d ; d = d->next) { ++ ++num_devices; ++ } ++ } ++ ++ ++num_devices; // +1 for NULL terminator ++ ++ supported = calloc(num_devices, sizeof(fpgad_supported_device)); ++ if (!supported) { ++ LOG("calloc failed.\n"); ++ return NULL; ++ } ++ ++ i = 0; ++ for (c = configurations ; c ; c = c->next) { ++ if (!c->enabled) // skip it ++ continue; ++ for (d = c->devices ; d ; d = d->next) { ++ fpgad_supported_device *dev = &supported[i++]; ++ ++ dev->vendor_id = d->vendor_id; ++ dev->device_id = d->device_id; ++ dev->library_path = cstr_dup(c->library); ++ dev->config = cstr_dup(c->configuration); ++ } ++ } ++ ++ for (c = configurations ; c ; ) { ++ cfg_plugin_configuration *ctrash = c; ++ ++ for (d = c->devices ; d ; ) { ++ cfg_vendor_device_id *dtrash = d; ++ d = d->next; ++ free(dtrash); ++ } ++ ++ c = c->next; ++ ++ if (ctrash->configuration) ++ free(ctrash->configuration); ++ if (ctrash->library) ++ free(ctrash->library); ++ free(ctrash); ++ } ++ ++ return supported; ++} ++ ++STATIC bool cfg_verify_supported_devices(fpgad_supported_device *d) ++{ ++ while (d->library_path) { ++ char *sub = NULL; ++ ++ if (d->library_path[0] == '/') { ++ LOG("plugin library paths may not " ++ "be absolute paths: %s\n", d->library_path); ++ return false; ++ } ++ ++ if (cmd_path_is_symlink(d->library_path)) { ++ LOG("plugin library paths may not " ++ "contain links: %s\n", d->library_path); ++ return false; ++ } ++ ++ sub = strstr((char *)d->library_path, ".."); ++ if (sub) { ++ LOG("plugin library paths may not " ++ "contain .. : %s\n", d->library_path); ++ return false; ++ } ++ ++ ++d; ++ } ++ ++ return true; ++} ++ ++int cfg_load_config(struct fpgad_config *c) ++{ ++ char *cfg_buf; ++ json_object *root = NULL; ++ json_object *j_configurations = NULL; ++ json_object *j_plugins = NULL; ++ enum json_tokener_error j_err = json_tokener_success; ++ int res = 1; ++ int num_plugins; ++ int i; ++ cfg_plugin_configuration *configurations = NULL; ++ ++ cfg_buf = cfg_read_file(c->cfgfile); ++ if (!cfg_buf) ++ return res; ++ ++ root = json_tokener_parse_verbose(cfg_buf, &j_err); ++ if (!root) { ++ LOG("error parsing %s: %s\n", ++ c->cfgfile, ++ json_tokener_error_desc(j_err)); ++ goto out_free; ++ } ++ ++ if (!json_object_object_get_ex(root, ++ "configurations", ++ &j_configurations)) { ++ LOG("failed to find configurations section in %s.\n", ++ c->cfgfile); ++ goto out_put; ++ } ++ ++ if (!json_object_object_get_ex(root, "plugins", &j_plugins)) { ++ LOG("failed to find plugins section in %s.\n", c->cfgfile); ++ goto out_put; ++ } ++ ++ if (!json_object_is_type(j_plugins, json_type_array)) { ++ LOG("'plugins' JSON object not array.\n"); ++ goto out_put; ++ } ++ ++ num_plugins = json_object_array_length(j_plugins); ++ for (i = 0 ; i < num_plugins ; ++i) { ++ json_object *j_plugin; ++ const char *plugin_name; ++ ++ j_plugin = json_object_array_get_idx(j_plugins, i); ++ plugin_name = json_object_get_string(j_plugin); ++ ++ if (cfg_process_plugin(plugin_name, ++ j_configurations, ++ &configurations)) ++ goto out_put; ++ } ++ ++ if (!configurations) { ++ LOG("no configurations found in %s.\n", c->cfgfile); ++ goto out_put; ++ } ++ ++ c->supported_devices = cfg_json_to_supported(configurations); ++ ++ if (c->supported_devices) { ++ ++ if (cfg_verify_supported_devices(c->supported_devices)) { ++ res = 0; ++ } else { ++ fpgad_supported_device *trash = c->supported_devices; ++ ++ LOG("invalid configuration file\n"); ++ ++ while (trash->library_path) { ++ free((void *)trash->library_path); ++ if (trash->config) ++ free((void *)trash->config); ++ ++ ++trash; ++ } ++ ++ free(c->supported_devices); ++ c->supported_devices = NULL; ++ } ++ ++ } ++ ++out_put: ++ json_object_put(root); ++out_free: ++ free(cfg_buf); ++ return res; ++} +diff --git a/tools/fpgad/config_file.h b/tools/fpgad/config_file.h +new file mode 100644 +index 00000000..0e476a30 +--- /dev/null ++++ b/tools/fpgad/config_file.h +@@ -0,0 +1,38 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_CONFIG_FILE_H__ ++#define __FPGAD_CONFIG_FILE_H__ ++ ++#include "fpgad.h" ++ ++// 0 on success ++int cfg_find_config_file(struct fpgad_config *c); ++ ++// 0 on success ++int cfg_load_config(struct fpgad_config *c); ++ ++#endif /* __FPGAD_CONFIG_FILE_H__ */ +diff --git a/tools/fpgad/daemonize.c b/tools/fpgad/daemonize.c +new file mode 100644 +index 00000000..79f05e50 +--- /dev/null ++++ b/tools/fpgad/daemonize.c +@@ -0,0 +1,98 @@ ++// Copyright(c) 2017-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++/* ++ * daemonize.c : routine to become a system daemon process. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int daemonize(void (*hndlr)(int, siginfo_t *, void *), mode_t mask, const char *dir) ++{ ++ pid_t pid; ++ pid_t sid; ++ int res; ++ int fd; ++ struct sigaction sa; ++ ++ pid = fork(); ++ if (pid < 0) // fork() failed. ++ return errno; ++ ++ // 1) Orphan the child process so that it runs in the background. ++ if (pid > 0) ++ exit(0); ++ ++ // 2) Become leader of a new session and process group leader of new process ++ // group. The process is now detached from its controlling terminal. ++ sid = setsid(); ++ if (sid < 0) ++ return errno; ++ ++ // 3) Establish signal handler. ++ memset(&sa, 0, sizeof(sa)); ++ sa.sa_flags = SA_SIGINFO | SA_RESETHAND; ++ sa.sa_sigaction = hndlr; ++ ++ res = sigaction(SIGINT, &sa, NULL); ++ if (res < 0) ++ return errno; ++ ++ res = sigaction(SIGTERM, &sa, NULL); ++ if (res < 0) ++ return errno; ++ ++ // 4) Orphan the child again - the session leading process terminates. ++ // (only session leaders can request TTY). ++ pid = fork(); ++ if (pid < 0) // fork() failed. ++ return errno; ++ ++ if (pid > 0) ++ exit(0); ++ ++ // 5) Set new file mode mask. ++ umask(mask); ++ ++ // 6) change directory ++ res = chdir(dir); ++ if (res < 0) ++ return errno; ++ ++ // 7) Close all open file descriptors ++ fd = sysconf(_SC_OPEN_MAX); ++ while (fd >= 0) ++ close(fd--); ++ ++ return 0; ++} +diff --git a/tools/fpgad/event_dispatcher_thread.c b/tools/fpgad/event_dispatcher_thread.c +new file mode 100644 +index 00000000..235a3263 +--- /dev/null ++++ b/tools/fpgad/event_dispatcher_thread.c +@@ -0,0 +1,266 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include ++#include ++#include "event_dispatcher_thread.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("event_dispatcher_thread: " format, ##__VA_ARGS__) ++ ++event_dispatcher_thread_config event_dispatcher_config = { ++ .global = &global_config, ++ .sched_policy = SCHED_RR, ++ .sched_priority = 30, ++}; ++ ++#define EVENT_DISPATCH_QUEUE_DEPTH 512 ++ ++typedef struct _evt_dispatch_queue { ++ event_dispatch_queue_item q[EVENT_DISPATCH_QUEUE_DEPTH]; ++ unsigned head; ++ unsigned tail; ++ pthread_mutex_t lock; ++} evt_dispatch_queue; ++ ++STATIC sem_t evt_dispatch_sem; ++ ++STATIC evt_dispatch_queue normal_queue = { ++ { { NULL, NULL, NULL }, }, ++ 0, ++ 0, ++ PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, ++}; ++ ++STATIC evt_dispatch_queue high_priority_queue = { ++ { { NULL, NULL, NULL }, }, ++ 0, ++ 0, ++ PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, ++}; ++ ++STATIC void evt_queue_init(evt_dispatch_queue *q) ++{ ++ memset(q->q, 0, sizeof(q->q)); ++ q->head = q->tail = 0; ++} ++ ++STATIC void evt_queue_destroy(evt_dispatch_queue *q) ++{ ++ q->head = q->tail = 0; ++} ++ ++STATIC volatile bool dispatcher_is_ready = false; ++ ++bool evt_dispatcher_is_ready(void) ++{ ++ return dispatcher_is_ready; ++} ++ ++STATIC bool evt_queue_is_full(evt_dispatch_queue *q) ++{ ++ const size_t num = sizeof(q->q) / sizeof(q->q[0]); ++ ++ if (q->tail > q->head) { ++ if ((q->head == 0) && (q->tail == (num - 1))) ++ return true; ++ } else if (q->tail < q->head) { ++ if (q->tail == (q->head - 1)) ++ return true; ++ } ++ return false; ++} ++ ++STATIC bool evt_queue_is_empty(evt_dispatch_queue *q) ++{ ++ return q->head == q->tail; ++} ++ ++STATIC bool _evt_queue_response(evt_dispatch_queue *q, ++ fpgad_respond_event_t callback, ++ fpgad_monitored_device *device, ++ void *context) ++{ ++ int res; ++ ++ opae_mutex_lock(res, &q->lock); ++ ++ if (evt_queue_is_full(q)) { ++ opae_mutex_unlock(res, &q->lock); ++ return false; ++ } ++ ++ q->q[q->tail].callback = callback; ++ q->q[q->tail].device = device; ++ q->q[q->tail].context = context; ++ ++ q->tail = (q->tail + 1) % EVENT_DISPATCH_QUEUE_DEPTH; ++ ++ opae_mutex_unlock(res, &q->lock); ++ ++ sem_post(&evt_dispatch_sem); ++ ++ return true; ++} ++ ++STATIC bool _evt_queue_get(evt_dispatch_queue *q, ++ event_dispatch_queue_item *item) ++{ ++ int res; ++ ++ opae_mutex_lock(res, &q->lock); ++ ++ if (evt_queue_is_empty(q)) { ++ opae_mutex_unlock(res, &q->lock); ++ return false; ++ } ++ ++ *item = q->q[q->head]; ++ memset(&q->q[q->head], 0, sizeof(q->q[0])); ++ q->head = (q->head + 1) % EVENT_DISPATCH_QUEUE_DEPTH; ++ ++ opae_mutex_unlock(res, &q->lock); ++ ++ return true; ++} ++ ++bool evt_queue_response(fpgad_respond_event_t callback, ++ fpgad_monitored_device *device, ++ void *context) ++{ ++ return _evt_queue_response(&normal_queue, ++ callback, ++ device, ++ context); ++} ++ ++bool evt_queue_get(event_dispatch_queue_item *item) ++{ ++ return _evt_queue_get(&normal_queue, item); ++} ++ ++bool evt_queue_response_high(fpgad_respond_event_t callback, ++ fpgad_monitored_device *device, ++ void *context) ++{ ++ return _evt_queue_response(&high_priority_queue, ++ callback, ++ device, ++ context); ++} ++ ++bool evt_queue_get_high(event_dispatch_queue_item *item) ++{ ++ return _evt_queue_get(&high_priority_queue, item); ++} ++ ++void *event_dispatcher_thread(void *thread_context) ++{ ++ event_dispatcher_thread_config *c = ++ (event_dispatcher_thread_config *)thread_context; ++ struct sched_param sched_param; ++ int policy = 0; ++ int res; ++ struct timespec ts; ++ ++ LOG("starting\n"); ++ ++ res = pthread_getschedparam(pthread_self(), &policy, &sched_param); ++ if (res) { ++ LOG("error getting scheduler params: %s\n", strerror(res)); ++ } else { ++ policy = c->sched_policy; ++ sched_param.sched_priority = c->sched_priority; ++ ++ res = pthread_setschedparam(pthread_self(), ++ policy, ++ &sched_param); ++ if (res) { ++ LOG("error setting scheduler params" ++ " (got root?): %s\n", strerror(res)); ++ } ++ } ++ ++ evt_queue_init(&normal_queue); ++ evt_queue_init(&high_priority_queue); ++ ++ if (sem_init(&evt_dispatch_sem, 0, 0)) { ++ LOG("failed to init queue sem.\n"); ++ goto out_exit; ++ } ++ ++ dispatcher_is_ready = true; ++ ++ while (c->global->running) { ++ ++ clock_gettime(CLOCK_REALTIME, &ts); ++ ++ ts.tv_nsec += c->global->poll_interval_usec * 1000; ++ if (ts.tv_nsec > 1000000000) { ++ ++ts.tv_sec; ++ ts.tv_nsec -= 1000000000; ++ } ++ ++ res = sem_timedwait(&evt_dispatch_sem, &ts); ++ ++ if (!res) { ++ event_dispatch_queue_item item; ++ ++ // Process all high-priority items first ++ while (evt_queue_get_high(&item)) { ++ LOG("dispatching (high) for object_id: 0x%" PRIx64 ".\n", ++ item.device->object_id); ++ item.callback(item.device, item.context); ++ } ++ ++ if (evt_queue_get(&item)) { ++ LOG("dispatching for object_id: 0x%" PRIx64 ".\n", ++ item.device->object_id); ++ item.callback(item.device, item.context); ++ } ++ } ++ ++ } ++ ++ dispatcher_is_ready = false; ++ ++ evt_queue_destroy(&normal_queue); ++ evt_queue_destroy(&high_priority_queue); ++ ++ sem_destroy(&evt_dispatch_sem); ++ ++out_exit: ++ LOG("exiting\n"); ++ return NULL; ++} +diff --git a/tools/fpgad/event_dispatcher_thread.h b/tools/fpgad/event_dispatcher_thread.h +new file mode 100644 +index 00000000..5fdff07b +--- /dev/null ++++ b/tools/fpgad/event_dispatcher_thread.h +@@ -0,0 +1,63 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_EVENT_DISPATCHER_THREAD_H__ ++#define __FPGAD_EVENT_DISPATCHER_THREAD_H__ ++ ++#include "fpgad.h" ++#include "monitored_device.h" ++ ++typedef struct _event_dispatcher_thread_config { ++ struct fpgad_config *global; ++ int sched_policy; ++ int sched_priority; ++} event_dispatcher_thread_config; ++ ++extern event_dispatcher_thread_config event_dispatcher_config; ++ ++void *event_dispatcher_thread(void *); ++ ++typedef struct _event_dispatch_queue_item { ++ fpgad_respond_event_t callback; ++ fpgad_monitored_device *device; ++ void *context; ++} event_dispatch_queue_item; ++ ++bool evt_dispatcher_is_ready(void); ++ ++bool evt_queue_response(fpgad_respond_event_t callback, ++ fpgad_monitored_device *device, ++ void *context); ++ ++bool evt_queue_get(event_dispatch_queue_item *item); ++ ++bool evt_queue_response_high(fpgad_respond_event_t callback, ++ fpgad_monitored_device *device, ++ void *context); ++ ++bool evt_queue_get_high(event_dispatch_queue_item *item); ++ ++#endif /* __FPGAD_EVENT_DISPATCHER_THREAD_H__ */ +diff --git a/tools/fpgad/events_api_thread.c b/tools/fpgad/events_api_thread.c +new file mode 100644 +index 00000000..19a9e134 +--- /dev/null ++++ b/tools/fpgad/events_api_thread.c +@@ -0,0 +1,296 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++#ifndef __USE_GNU ++#define __USE_GNU ++#endif ++ ++#include ++#include ++#include ++#include ++#include "events_api_thread.h" ++#include "api/opae_events_api.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("events_api_thread: " format, ##__VA_ARGS__) ++ ++events_api_thread_config events_api_config = { ++ .global = &global_config, ++ .sched_policy = SCHED_RR, ++ .sched_priority = 10, ++}; ++ ++#define MAX_CLIENT_CONNECTIONS 1023 ++#define SRV_SOCKET 0 ++#define FIRST_CLIENT_SOCKET 1 ++ ++/* array keeping track of all connection file descriptors (plus server socket) */ ++STATIC struct pollfd pollfds[MAX_CLIENT_CONNECTIONS+1]; ++STATIC nfds_t num_fds = 1; ++ ++STATIC void remove_client(int conn_socket) ++{ ++ nfds_t i, j; ++ nfds_t removed = 0; ++ ++ opae_api_unregister_all_events_for(conn_socket); ++ LOG("closing connection conn_socket=%d.\n", conn_socket); ++ close(conn_socket); ++ ++ for (i = j = FIRST_CLIENT_SOCKET ; i < num_fds ; ++i) { ++ if (conn_socket != pollfds[i].fd) { ++ if (j != i) ++ pollfds[j] = pollfds[i]; ++ ++j; ++ } else { ++ ++removed; ++ } ++ } ++ ++ num_fds -= removed; ++} ++ ++STATIC int handle_message(int conn_socket) ++{ ++ struct msghdr mh; ++ struct cmsghdr *cmh; ++ struct iovec iov[1]; ++ struct event_request req; ++ char buf[CMSG_SPACE(sizeof(int))]; ++ ssize_t n; ++ int *fd_ptr; ++ ++ /* set up ancillary data message header */ ++ iov[0].iov_base = &req; ++ iov[0].iov_len = sizeof(req); ++ memset(buf, 0, sizeof(buf)); ++ mh.msg_name = NULL; ++ mh.msg_namelen = 0; ++ mh.msg_iov = iov; ++ mh.msg_iovlen = sizeof(iov) / sizeof(iov[0]); ++ mh.msg_control = buf; ++ mh.msg_controllen = CMSG_LEN(sizeof(int)); ++ mh.msg_flags = 0; ++ cmh = CMSG_FIRSTHDR(&mh); ++ cmh->cmsg_len = CMSG_LEN(sizeof(int)); ++ cmh->cmsg_level = SOL_SOCKET; ++ cmh->cmsg_type = SCM_RIGHTS; ++ ++ n = recvmsg(conn_socket, &mh, 0); ++ if (n < 0) { ++ LOG("recvmsg() failed: %s\n", strerror(errno)); ++ return (int)n; ++ } ++ ++ if (!n) { // socket closed by peer ++ remove_client(conn_socket); ++ return (int)n; ++ } ++ ++ switch (req.type) { ++ ++ case REGISTER_EVENT: ++ fd_ptr = (int *)CMSG_DATA(cmh); ++ ++ if (opae_api_register_event(conn_socket, *fd_ptr, ++ req.event, req.object_id)) { ++ LOG("failed to register event\n"); ++ return -1; ++ } ++ ++ LOG("registered event sock=%d:fd=%d" ++ "(event=%d object_id=0x%" PRIx64 ")\n", ++ conn_socket, *fd_ptr, req.event, req.object_id); ++ ++ break; ++ ++ case UNREGISTER_EVENT: ++ ++ if (opae_api_unregister_event(conn_socket, ++ req.event, ++ req.object_id)) { ++ LOG("failed to unregister event\n"); ++ return -1; ++ } ++ ++ LOG("unregistered event sock=%d:" ++ "(event=%d object_id=0x%" PRIx64 ")\n", ++ conn_socket, req.event, req.object_id); ++ ++ break; ++ ++ default: ++ LOG("unknown request type %d\n", req.type); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++STATIC volatile bool evt_api_is_ready = false; ++ ++bool events_api_is_ready(void) ++{ ++ return evt_api_is_ready; ++} ++ ++void *events_api_thread(void *thread_context) ++{ ++ events_api_thread_config *c = ++ (events_api_thread_config *)thread_context; ++ struct sched_param sched_param; ++ int policy = 0; ++ int res; ++ ++ nfds_t i; ++ struct sockaddr_un addr; ++ int server_socket; ++ int conn_socket; ++ size_t len; ++ ++ LOG("starting\n"); ++ ++ res = pthread_getschedparam(pthread_self(), &policy, &sched_param); ++ if (res) { ++ LOG("error getting scheduler params: %s\n", strerror(res)); ++ } else { ++ policy = c->sched_policy; ++ sched_param.sched_priority = c->sched_priority; ++ ++ res = pthread_setschedparam(pthread_self(), ++ policy, ++ &sched_param); ++ if (res) { ++ LOG("error setting scheduler params" ++ " (got root?): %s\n", strerror(res)); ++ } ++ } ++ ++ unlink(c->global->api_socket); ++ ++ server_socket = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (server_socket < 0) { ++ LOG("failed to create server socket.\n"); ++ goto out_exit; ++ } ++ LOG("created server socket.\n"); ++ ++ addr.sun_family = AF_UNIX; ++ ++ len = strnlen(c->global->api_socket, sizeof(addr.sun_path) - 1); ++ memcpy(addr.sun_path, c->global->api_socket, len); ++ addr.sun_path[len] = '\0'; ++ ++ if (bind(server_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { ++ LOG("failed to bind server socket.\n"); ++ goto out_close_server; ++ } ++ LOG("server socket bind success.\n"); ++ ++ if (listen(server_socket, 20) < 0) { ++ LOG("failed to listen on socket.\n"); ++ goto out_close_server; ++ } ++ LOG("listening for connections.\n"); ++ ++ evt_api_is_ready = true; ++ ++ pollfds[SRV_SOCKET].fd = server_socket; ++ pollfds[SRV_SOCKET].events = POLLIN | POLLPRI; ++ num_fds = 1; ++ ++ while (c->global->running) { ++ ++ res = poll(pollfds, num_fds, 100); ++ if (res < 0) { ++ LOG("poll error\n"); ++ continue; ++ } ++ ++ if (0 == res) // timeout ++ continue; ++ ++ if ((nfds_t)res > num_fds) { // weird ++ LOG("something bad happened during poll!\n"); ++ continue; ++ } ++ ++ // handle requests on existing sockets ++ for (i = FIRST_CLIENT_SOCKET ; i < num_fds ; ++i) { ++ if (pollfds[i].revents) { ++ handle_message(pollfds[i].fd); ++ } ++ } ++ ++ // handle new connection requests ++ if (pollfds[SRV_SOCKET].revents) { ++ ++ if (num_fds == MAX_CLIENT_CONNECTIONS+1) { ++ LOG("exceeded max connections!\n"); ++ continue; ++ } ++ ++ conn_socket = accept(server_socket, NULL, NULL); ++ ++ if (conn_socket < 0) { ++ LOG("failed to accept new connection!\n"); ++ } else { ++ LOG("accepting connection %d.\n", conn_socket); ++ ++ pollfds[num_fds].fd = conn_socket; ++ pollfds[num_fds].events = POLLIN | POLLPRI; ++ ++num_fds; ++ } ++ ++ } ++ ++ } ++ ++ opae_api_unregister_all_events(); ++ ++ // close any active client sockets ++ for (i = FIRST_CLIENT_SOCKET ; i < num_fds ; ++i) { ++ close(pollfds[i].fd); ++ } ++ ++out_close_server: ++ evt_api_is_ready = false; ++ close(server_socket); ++out_exit: ++ LOG("exiting\n"); ++ return NULL; ++} +diff --git a/tools/fpgad/events_api_thread.h b/tools/fpgad/events_api_thread.h +new file mode 100644 +index 00000000..c097a4dc +--- /dev/null ++++ b/tools/fpgad/events_api_thread.h +@@ -0,0 +1,43 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_EVENTS_API_THREAD_H__ ++#define __FPGAD_EVENTS_API_THREAD_H__ ++ ++#include "fpgad.h" ++#include ++ ++typedef struct _events_api_thread_config { ++ struct fpgad_config *global; ++ int sched_policy; ++ int sched_priority; ++} events_api_thread_config; ++ ++extern events_api_thread_config events_api_config; ++ ++void *events_api_thread(void *); ++ ++#endif /* __FPGAD_EVENTS_API_THREAD_H__ */ +diff --git a/tools/fpgad/fpgad.c b/tools/fpgad/fpgad.c +new file mode 100644 +index 00000000..ff88525a +--- /dev/null ++++ b/tools/fpgad/fpgad.c +@@ -0,0 +1,192 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include "fpgad.h" ++#include "monitor_thread.h" ++#include "event_dispatcher_thread.h" ++#include "events_api_thread.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("main: " format, ##__VA_ARGS__) ++ ++struct fpgad_config global_config; ++ ++void sig_handler(int sig, siginfo_t *info, void *unused) ++{ ++ UNUSED_PARAM(info); ++ UNUSED_PARAM(unused); ++ switch (sig) { ++ case SIGINT: ++ // Process interrupted. ++ LOG("Got SIGINT. Exiting.\n"); ++ global_config.running = false; ++ break; ++ case SIGTERM: ++ // Process terminated. ++ LOG("Got SIGTERM. Exiting.\n"); ++ global_config.running = false; ++ break; ++ } ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int res; ++ FILE *fp; ++ ++ memset(&global_config, 0, sizeof(global_config)); ++ ++ global_config.poll_interval_usec = 100 * 1000; ++ global_config.running = true; ++ global_config.api_socket = "/tmp/fpga_event_socket"; ++ global_config.num_null_gbs = 0; ++ ++ log_set(stdout); ++ ++ res = cmd_parse_args(&global_config, argc, argv); ++ if (res != 0) { ++ if (res == -2) ++ res = 0; ++ else ++ LOG("error parsing command line.\n"); ++ goto out_destroy; ++ } ++ ++ if (cmd_canonicalize_paths(&global_config)) { ++ LOG("error with paths.\n"); ++ goto out_destroy; ++ } ++ ++ if (global_config.daemon) { ++ ++ res = daemonize(sig_handler, ++ global_config.filemode, ++ global_config.directory); ++ if (res != 0) { ++ LOG("daemonize failed: %s\n", strerror(res)); ++ goto out_destroy; ++ } ++ ++ ++ } else { ++ struct sigaction sa; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.sa_flags = SA_SIGINFO | SA_RESETHAND; ++ sa.sa_sigaction = sig_handler; ++ ++ res = sigaction(SIGINT, &sa, NULL); ++ if (res < 0) { ++ LOG("failed to register SIGINT handler.\n"); ++ goto out_destroy; ++ } ++ ++ res = sigaction(SIGTERM, &sa, NULL); ++ if (res < 0) { ++ LOG("failed to register SIGTERM handler.\n"); ++ goto out_destroy; ++ } ++ } ++ ++ if (log_open(global_config.logfile) < 0) { ++ LOG("failed to open log file\n"); ++ res = 1; ++ goto out_destroy; ++ } ++ ++ fp = fopen(global_config.pidfile, "w"); ++ if (NULL == fp) { ++ LOG("failed to open pid file\n"); ++ res = 1; ++ goto out_destroy; ++ } ++ fprintf(fp, "%d\n", getpid()); ++ fclose(fp); ++ ++ res = mon_enumerate(&global_config); ++ if (res) { ++ LOG("OPAE device enumeration failed\n"); ++ goto out_destroy; ++ } ++ ++ res = pthread_create(&global_config.event_dispatcher_thr, ++ NULL, ++ event_dispatcher_thread, ++ &event_dispatcher_config); ++ if (res) { ++ LOG("failed to create event_dispatcher_thread\n"); ++ global_config.running = false; ++ goto out_destroy; ++ } ++ ++ while (!evt_dispatcher_is_ready()) ++ usleep(1); ++ ++ res = pthread_create(&global_config.monitor_thr, ++ NULL, ++ monitor_thread, ++ &monitor_config); ++ if (res) { ++ LOG("failed to create monitor_thread\n"); ++ global_config.running = false; ++ goto out_stop_event_dispatcher; ++ } ++ ++ res = pthread_create(&global_config.events_api_thr, ++ NULL, ++ events_api_thread, ++ &events_api_config); ++ if (res) { ++ LOG("failed to create events_api_thread\n"); ++ global_config.running = false; ++ goto out_stop_monitor; ++ } ++ ++ if (pthread_join(global_config.events_api_thr, NULL)) { ++ LOG("failed to join events_api_thread\n"); ++ } ++out_stop_monitor: ++ if (pthread_join(global_config.monitor_thr, NULL)) { ++ LOG("failed to join monitor_thread\n"); ++ } ++out_stop_event_dispatcher: ++ if (pthread_join(global_config.event_dispatcher_thr, NULL)) { ++ LOG("failed to join event_dispatcher_thread\n"); ++ } ++out_destroy: ++ mon_destroy(&global_config); ++ cmd_destroy(&global_config); ++ log_close(); ++ return res; ++} +diff --git a/tools/fpgad/fpgad.cfg b/tools/fpgad/fpgad.cfg +new file mode 100644 +index 00000000..41a656e6 +--- /dev/null ++++ b/tools/fpgad/fpgad.cfg +@@ -0,0 +1,38 @@ ++{ ++ "configurations": { ++ "fpgad-xfpga": { ++ "configuration": { ++ }, ++ "enabled": true, ++ "plugin": "libfpgad-xfpga.so", ++ "devices": [ ++ [ "0x8086", "0xbcc0" ], ++ [ "0x8086", "0xbcc1" ] ++ ] ++ }, ++ "fpgad-vc": { ++ "configuration": { ++ "cool-down": 30, ++ "config-sensors-enabled": true, ++ "sensors": [ ++ { ++ "id": 25, ++ "low-warn": 11.40, ++ "low-fatal": 10.56 ++ } ++ ] ++ }, ++ "enabled": true, ++ "plugin": "libfpgad-vc.so", ++ "devices": [ ++ [ "0x8086", "0x0b30" ], ++ [ "0x8086", "0x0b31" ] ++ ] ++ } ++ }, ++ ++ "plugins": [ ++ "fpgad-xfpga", ++ "fpgad-vc" ++ ] ++} +diff --git a/tools/fpgad/fpgad.conf b/tools/fpgad/fpgad.conf +new file mode 100644 +index 00000000..7fad49d9 +--- /dev/null ++++ b/tools/fpgad/fpgad.conf +@@ -0,0 +1,4 @@ ++# Intel FPGA daemon variables ++PIDFILE=fpgad.pid ++LOGFILE=fpgad.log ++CFGFILE=/etc/opae/fpgad.cfg +diff --git a/tools/fpgad/fpgad.h b/tools/fpgad/fpgad.h +new file mode 100644 +index 00000000..ed501096 +--- /dev/null ++++ b/tools/fpgad/fpgad.h +@@ -0,0 +1,76 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_FPGAD_H__ ++#define __FPGAD_FPGAD_H__ ++ ++#ifndef __USE_GNU ++#define __USE_GNU ++#endif ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "opae_int.h" ++ ++#include "api/logging.h" ++#include "command_line.h" ++ ++int daemonize(void (*hndlr)(int, siginfo_t *, void *), ++ mode_t mask, ++ const char *dir); ++ ++ ++#define fpgad_mutex_lock(__res, __mtx_ptr) \ ++ ({ \ ++ (__res) = pthread_mutex_lock(__mtx_ptr); \ ++ if (__res) \ ++ LOG("pthread_mutex_lock failed: %s", \ ++ strerror(errno)); \ ++ __res; \ ++ }) ++ ++#define fpgad_mutex_unlock(__res, __mtx_ptr) \ ++ ({ \ ++ (__res) = pthread_mutex_unlock(__mtx_ptr); \ ++ if (__res) \ ++ LOG("pthread_mutex_unlock failed: %s", \ ++ strerror(errno)); \ ++ __res; \ ++ }) ++ ++#endif /* __FPGAD_FPGAD_H__ */ +diff --git a/tools/fpgad/fpgad.service.in b/tools/fpgad/fpgad.service.in +new file mode 100644 +index 00000000..0ec5bac1 +--- /dev/null ++++ b/tools/fpgad/fpgad.service.in +@@ -0,0 +1,21 @@ ++# Intel FPGA daemon ++[Unit] ++Description = FPGA monitor ++ ++[Service] ++Type=simple ++StandardOutput=journal+console ++StandardError=journal+console ++EnvironmentFile=-/etc/sysconfig/fpgad.conf ++Restart=always ++RestartSec=10s ++SendSIGHUP=yes ++KillSignal=SIGHUP ++ExecStart=@CMAKE_INSTALL_PREFIX@/bin/fpgad \ ++ -l $LOGFILE \ ++ -p $PIDFILE \ ++ -c $CFGFILE ++RestartPreventExitStatus=1 ++ ++[Install] ++WantedBy=multi-user.target +diff --git a/tools/fpgad/monitor_thread.c b/tools/fpgad/monitor_thread.c +new file mode 100644 +index 00000000..d4cfd9cd +--- /dev/null ++++ b/tools/fpgad/monitor_thread.c +@@ -0,0 +1,261 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include "monitored_device.h" ++#include "monitor_thread.h" ++#include "event_dispatcher_thread.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("monitor_thread: " format, ##__VA_ARGS__) ++ ++monitor_thread_config monitor_config = { ++ .global = &global_config, ++ .sched_policy = SCHED_RR, ++ .sched_priority = 20, ++}; ++ ++STATIC pthread_mutex_t mon_list_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; ++STATIC fpgad_monitored_device *monitored_device_list; ++ ++STATIC void mon_queue_response(fpgad_detection_status status, ++ fpgad_respond_event_t response, ++ fpgad_monitored_device *d, ++ void *response_context) ++{ ++ if (status == FPGAD_STATUS_DETECTED_HIGH) { ++ ++ if (evt_queue_response_high(response, ++ d, ++ response_context)) { ++ pthread_yield(); ++ } else { ++ LOG("high priority event queue is full. Dropping!\n"); ++ } ++ ++ } else if (status == FPGAD_STATUS_DETECTED) { ++ ++ if (evt_queue_response(response, ++ d, ++ response_context)) { ++ pthread_yield(); ++ } else { ++ LOG("event queue is full. Dropping!\n"); ++ } ++ ++ } ++} ++ ++STATIC void mon_monitor(fpgad_monitored_device *d) ++{ ++ unsigned i; ++ ++ if (!d->detections) ++ return; ++ ++ for (i = 0 ; d->detections[i] ; ++i) { ++ fpgad_detection_status result; ++ fpgad_detect_event_t detect = ++ d->detections[i]; ++ void *detect_context = ++ d->detection_contexts ? ++ d->detection_contexts[i] : NULL; ++ ++ result = detect(d, detect_context); ++ ++ if (result != FPGAD_STATUS_NOT_DETECTED && d->responses) { ++ fpgad_respond_event_t response = ++ d->responses[i]; ++ void *response_context = ++ d->response_contexts ? ++ d->response_contexts[i] : NULL; ++ ++ if (response) { ++ mon_queue_response(result, ++ response, ++ d, ++ response_context); ++ } ++ } ++ } ++} ++ ++STATIC volatile bool mon_is_ready = false; ++ ++bool monitor_is_ready(void) ++{ ++ return mon_is_ready; ++} ++ ++void *monitor_thread(void *thread_context) ++{ ++ monitor_thread_config *c = (monitor_thread_config *)thread_context; ++ struct sched_param sched_param; ++ int policy = 0; ++ int res; ++ int err; ++ fpgad_monitored_device *d; ++ ++ LOG("starting\n"); ++ ++ res = pthread_getschedparam(pthread_self(), &policy, &sched_param); ++ if (res) { ++ LOG("error getting scheduler params: %s\n", strerror(res)); ++ } else { ++ policy = c->sched_policy; ++ sched_param.sched_priority = c->sched_priority; ++ ++ res = pthread_setschedparam(pthread_self(), ++ policy, ++ &sched_param); ++ if (res) { ++ LOG("error setting scheduler params" ++ " (got root?): %s\n", strerror(res)); ++ } ++ } ++ ++ mon_is_ready = true; ++ ++ while (c->global->running) { ++ fpgad_mutex_lock(err, &mon_list_lock); ++ ++ for (d = monitored_device_list ; d ; d = d->next) { ++ mon_monitor(d); ++ } ++ ++ fpgad_mutex_unlock(err, &mon_list_lock); ++ ++ usleep(c->global->poll_interval_usec); ++ } ++ ++ while (evt_dispatcher_is_ready()) { ++ // Wait for the event dispatcher to complete ++ // before we destroy the monitored devices. ++ usleep(c->global->poll_interval_usec); ++ } ++ ++ mon_destroy(c->global); ++ mon_is_ready = false; ++ ++ LOG("exiting\n"); ++ return NULL; ++} ++ ++void mon_monitor_device(fpgad_monitored_device *d) ++{ ++ int err; ++ fpgad_monitored_device *trav; ++ ++ fpgad_mutex_lock(err, &mon_list_lock); ++ ++ d->next = NULL; ++ ++ if (!monitored_device_list) { ++ monitored_device_list = d; ++ goto out_unlock; ++ } ++ ++ for (trav = monitored_device_list ; ++ trav->next ; ++ trav = trav->next) ++ /* find the end of the list */ ; ++ ++ trav->next = d; ++ ++out_unlock: ++ fpgad_mutex_unlock(err, &mon_list_lock); ++} ++ ++void mon_destroy(struct fpgad_config *c) ++{ ++ unsigned i; ++ int err; ++ fpgad_monitored_device *d; ++ ++ fpgad_mutex_lock(err, &mon_list_lock); ++ ++ for (d = monitored_device_list ; d ; ) { ++ fpgad_monitored_device *trash = d; ++ fpgad_plugin_destroy_t destroy; ++ ++ d = d->next; ++ ++ if (trash->type == FPGAD_PLUGIN_TYPE_THREAD) { ++ ++ if (trash->thread_stop_fn) { ++ trash->thread_stop_fn(); ++ } else { ++ LOG("Thread plugin \"%s\" has" ++ " no thread_stop_fn\n", ++ trash->supported->library_path); ++ pthread_cancel(trash->thread); ++ } ++ ++ pthread_join(trash->thread, NULL); ++ } ++ ++ destroy = (fpgad_plugin_destroy_t) ++ dlsym(trash->supported->dl_handle, ++ FPGAD_PLUGIN_DESTROY); ++ ++ if (destroy) { ++ destroy(trash); ++ } else { ++ LOG("warning - no destructor for \"%s\"\n", ++ trash->supported->library_path); ++ } ++ ++ if (trash->token) ++ fpgaDestroyToken(&trash->token); ++ ++ free(trash); ++ } ++ monitored_device_list = NULL; ++ ++ if (c->supported_devices) { ++ ++ for (i = 0 ; c->supported_devices[i].library_path ; ++i) { ++ fpgad_supported_device *d = &c->supported_devices[i]; ++ ++ if (d->flags & FPGAD_DEV_LOADED) { ++ dlclose(d->dl_handle); ++ } ++ ++ d->flags = 0; ++ d->dl_handle = NULL; ++ } ++ ++ } ++ ++ fpgad_mutex_unlock(err, &mon_list_lock); ++} +diff --git a/tools/fpgad/monitor_thread.h b/tools/fpgad/monitor_thread.h +new file mode 100644 +index 00000000..407e179e +--- /dev/null ++++ b/tools/fpgad/monitor_thread.h +@@ -0,0 +1,50 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_MONITOR_THREAD_H__ ++#define __FPGAD_MONITOR_THREAD_H__ ++ ++#include "fpgad.h" ++#include "monitored_device.h" ++ ++typedef struct _monitor_thread_config { ++ struct fpgad_config *global; ++ int sched_policy; ++ int sched_priority; ++} monitor_thread_config; ++ ++extern monitor_thread_config monitor_config; ++ ++void *monitor_thread(void *); ++ ++// 0 on success ++int mon_enumerate(struct fpgad_config *c); ++ ++void mon_destroy(struct fpgad_config *c); ++ ++void mon_monitor_device(fpgad_monitored_device *d); ++ ++#endif /* __FPGAD_MONITOR_THREAD_H__ */ +diff --git a/tools/fpgad/monitored_device.c b/tools/fpgad/monitored_device.c +new file mode 100644 +index 00000000..9860c8a4 +--- /dev/null ++++ b/tools/fpgad/monitored_device.c +@@ -0,0 +1,394 @@ ++// Copyright(c) 2018-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "monitored_device.h" ++#include "monitor_thread.h" ++#include "api/sysfs.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("monitored_device: " format, ##__VA_ARGS__) ++ ++fpgad_supported_device default_supported_devices_table[] = { ++ { 0x8086, 0xbcc0, "libfpgad-xfpga.so", 0, NULL, "" }, ++ { 0x8086, 0xbcc1, "libfpgad-xfpga.so", 0, NULL, "" }, ++ { 0x8086, 0x0b30, "libfpgad-vc.so", 0, NULL, "" }, ++ { 0x8086, 0x0b31, "libfpgad-vc.so", 0, NULL, "" }, ++ { 0, 0, NULL, 0, NULL, "" }, ++}; ++ ++STATIC fpgad_supported_device *mon_is_loaded(struct fpgad_config *c, ++ const char *library_path) ++{ ++ unsigned i; ++ int res = 0; ++ ++ for (i = 0 ; c->supported_devices[i].library_path ; ++i) { ++ fpgad_supported_device *d = &c->supported_devices[i]; ++ ++ res = strcmp(library_path, d->library_path); ++ ++ if (!res && (d->flags & FPGAD_DEV_LOADED)) ++ return d; ++ } ++ return NULL; ++} ++ ++STATIC fpgad_monitored_device * ++allocate_monitored_device(struct fpgad_config *config, ++ fpgad_supported_device *supported, ++ fpga_token token, ++ uint64_t object_id, ++ fpga_objtype object_type, ++ opae_bitstream_info *bitstr) ++{ ++ fpgad_monitored_device *d; ++ ++ d = (fpgad_monitored_device *) calloc( ++ 1, sizeof(fpgad_monitored_device)); ++ ++ if (!d) { ++ LOG("out of memory"); ++ return NULL; ++ } ++ ++ d->config = config; ++ d->supported = supported; ++ d->token = token; ++ d->object_id = object_id; ++ d->object_type = object_type; ++ d->bitstr = bitstr; ++ ++ return d; ++} ++ ++STATIC void *mon_find_plugin(const char *libpath) ++{ ++ char plugin_path[PATH_MAX]; ++ const char *search_paths[] = { OPAE_MODULE_SEARCH_PATHS }; ++ unsigned i; ++ void *dl_handle; ++ ++ for (i = 0 ; ++ i < sizeof(search_paths) / sizeof(search_paths[0]) ; ++ ++i) { ++ snprintf(plugin_path, sizeof(plugin_path), ++ "%s%s", search_paths[i], libpath); ++ ++ dl_handle = dlopen(plugin_path, RTLD_LAZY|RTLD_LOCAL); ++ if (dl_handle) ++ return dl_handle; ++ } ++ ++ return NULL; ++} ++ ++STATIC bool mon_consider_device(struct fpgad_config *c, fpga_token token) ++{ ++ unsigned i; ++ fpga_properties props = NULL; ++ fpga_token parent = NULL; ++ fpga_properties parent_props = NULL; ++ fpga_result res; ++ uint16_t vendor_id; ++ uint16_t device_id; ++ uint64_t object_id; ++ fpga_objtype object_type; ++ opae_bitstream_info *bitstr = NULL; ++ fpga_guid pr_ifc_id; ++ bool added = false; ++ ++ res = fpgaGetProperties(token, &props); ++ if (res != FPGA_OK) { ++ LOG("failed to get properties\n"); ++ return false; ++ } ++ ++ vendor_id = 0; ++ res = fpgaPropertiesGetVendorID(props, &vendor_id); ++ if (res != FPGA_OK) { ++ LOG("failed to get vendor ID\n"); ++ goto err_out_destroy; ++ } ++ ++ device_id = 0; ++ res = fpgaPropertiesGetDeviceID(props, &device_id); ++ if (res != FPGA_OK) { ++ LOG("failed to get device ID\n"); ++ goto err_out_destroy; ++ } ++ ++ object_id = 0; ++ res = fpgaPropertiesGetObjectID(props, &object_id); ++ if (res != FPGA_OK) { ++ LOG("failed to get object ID\n"); ++ goto err_out_destroy; ++ } ++ ++ object_type = FPGA_ACCELERATOR; ++ res = fpgaPropertiesGetObjectType(props, &object_type); ++ if (res != FPGA_OK) { ++ LOG("failed to get object type\n"); ++ goto err_out_destroy; ++ } ++ ++ // Do we have a NULL GBS from the command line ++ // that matches this device? ++ ++ if (object_type == FPGA_DEVICE) { ++ // The token's guid is the PR interface ID. ++ ++ res = fpgaPropertiesGetGUID(props, &pr_ifc_id); ++ if (res != FPGA_OK) { ++ LOG("failed to get PR interface ID\n");\ ++ goto err_out_destroy; ++ } ++ ++ for (i = 0 ; i < c->num_null_gbs ; ++i) { ++ if (!uuid_compare(c->null_gbs[i].pr_interface_id, ++ pr_ifc_id)) { ++ bitstr = &c->null_gbs[i]; ++ break; ++ } ++ } ++ } else { ++ // The parent token's guid is the PR interface ID. ++ ++ res = fpgaPropertiesGetParent(props, &parent); ++ if (res != FPGA_OK) { ++ LOG("failed to get parent token\n"); ++ goto err_out_destroy; ++ } ++ ++ res = fpgaGetProperties(parent, &parent_props); ++ if (res != FPGA_OK) { ++ LOG("failed to get parent properties\n"); ++ goto err_out_destroy; ++ } ++ ++ res = fpgaPropertiesGetGUID(parent_props, &pr_ifc_id); ++ if (res != FPGA_OK) { ++ LOG("failed to get PR interface ID\n"); ++ goto err_out_destroy; ++ } ++ ++ fpgaDestroyProperties(&parent_props); ++ fpgaDestroyToken(&parent); ++ ++ for (i = 0 ; i < c->num_null_gbs ; ++i) { ++ if (!uuid_compare(c->null_gbs[i].pr_interface_id, ++ pr_ifc_id)) { ++ bitstr = &c->null_gbs[i]; ++ break; ++ } ++ } ++ } ++ ++ fpgaDestroyProperties(&props); ++ ++ for (i = 0 ; c->supported_devices[i].library_path ; ++i) { ++ fpgad_supported_device *d = &c->supported_devices[i]; ++ ++ // Do we support this device? ++ if (d->vendor_id == vendor_id && ++ d->device_id == device_id) { ++ fpgad_supported_device *loaded_by; ++ fpgad_monitored_device *monitored; ++ fpgad_plugin_configure_t cfg; ++ int res; ++ ++ d->flags |= FPGAD_DEV_DETECTED; ++ ++ // Is the fpgad plugin already loaded? ++ loaded_by = mon_is_loaded(c, d->library_path); ++ ++ if (loaded_by) { ++ // The two table entries will share the ++ // same plugin handle (but only loaded_by ++ // will have FPGAD_DEV_LOADED). ++ d->dl_handle = loaded_by->dl_handle; ++ } else { ++ // Plugin hasn't been loaded. ++ // Load it now. ++ d->dl_handle = ++ mon_find_plugin(d->library_path); ++ if (!d->dl_handle) { ++ char *err = dlerror(); ++ LOG("failed to load \"%s\" %s\n", ++ d->library_path, ++ err ? err : ""); ++ continue; ++ } ++ ++ d->flags |= FPGAD_DEV_LOADED; ++ } ++ ++ if (!bitstr) { ++ LOG("Warning: no NULL GBS for vid=0x%04x " ++ "did=0x%04x objid=0x%x (%s)\n", ++ vendor_id, ++ device_id, ++ object_id, ++ object_type == FPGA_ACCELERATOR ? ++ "accelerator" : "device"); ++ } ++ ++ // Add the device to the monitored list. ++ monitored = allocate_monitored_device(c, ++ d, ++ token, ++ object_id, ++ object_type, ++ bitstr); ++ if (!monitored) { ++ LOG("failed to add device 0x%04x:0x%04x\n", ++ vendor_id, device_id); ++ continue; ++ } ++ ++ // Success ++ cfg = (fpgad_plugin_configure_t) ++ dlsym(d->dl_handle, ++ FPGAD_PLUGIN_CONFIGURE); ++ if (!cfg) { ++ LOG("failed to find %s in \"%s\"\n", ++ FPGAD_PLUGIN_CONFIGURE, ++ d->library_path); ++ free(monitored); ++ continue; ++ } ++ ++ res = cfg(monitored, d->config); ++ if (res) { ++ LOG("%s for \"%s\" returned %d.\n", ++ FPGAD_PLUGIN_CONFIGURE, ++ d->library_path, ++ res); ++ free(monitored); ++ continue; ++ } ++ ++ if (monitored->type == FPGAD_PLUGIN_TYPE_THREAD) { ++ ++ if (monitored->thread_fn) { ++ ++ if (pthread_create(&monitored->thread, ++ NULL, ++ monitored->thread_fn, ++ monitored)) { ++ LOG("failed to create thread" ++ " for \"%s\"\n", ++ d->library_path); ++ free(monitored); ++ continue; ++ } ++ ++ } else { ++ LOG("Thread plugin \"%s\" has no " ++ "thread_fn\n", d->library_path); ++ free(monitored); ++ continue; ++ } ++ ++ } ++ ++ mon_monitor_device(monitored); ++ added = true; ++ break; ++ } ++ } ++ ++ return added; ++ ++err_out_destroy: ++ if (props) ++ fpgaDestroyProperties(&props); ++ if (parent) ++ fpgaDestroyToken(&parent); ++ if (parent_props) ++ fpgaDestroyProperties(&parent_props); ++ return false; ++} ++ ++int mon_enumerate(struct fpgad_config *c) ++{ ++ fpga_token *tokens = NULL; ++ fpga_result res; ++ uint32_t num_matches = 0; ++ uint32_t i; ++ unsigned monitored_devices = 0; ++ ++ res = fpgaEnumerate(NULL, 0, NULL, 0, &num_matches); ++ if (res != FPGA_OK) { ++ LOG("enumeration failed\n"); ++ return res; ++ } ++ ++ if (!num_matches) { ++ res = 1; ++ return res; ++ } ++ ++ tokens = calloc(num_matches, sizeof(fpga_token)); ++ if (!tokens) { ++ res = 1; ++ LOG("out of memory\n"); ++ return res; ++ } ++ ++ res = fpgaEnumerate(NULL, 0, tokens, num_matches, &num_matches); ++ if (res != FPGA_OK) { ++ LOG("enumeration failed (2)\n"); ++ goto out_exit; ++ } ++ ++ for (i = 0 ; i < num_matches ; ++i) { ++ if (!mon_consider_device(c, tokens[i])) { ++ // Not monitoring it. Destroy the token. ++ fpgaDestroyToken(&tokens[i]); ++ } else { ++ ++monitored_devices; ++ } ++ } ++ ++out_exit: ++ if (tokens) ++ free(tokens); ++ return res + (monitored_devices ? 0 : 1); ++} +diff --git a/tools/fpgad/monitored_device.h b/tools/fpgad/monitored_device.h +new file mode 100644 +index 00000000..c2ef50e5 +--- /dev/null ++++ b/tools/fpgad/monitored_device.h +@@ -0,0 +1,123 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef __FPGAD_MONITORED_DEVICE_H__ ++#define __FPGAD_MONITORED_DEVICE_H__ ++ ++#include "fpgad.h" ++ ++typedef struct _fpgad_supported_device { ++ uint16_t vendor_id; ++ uint16_t device_id; ++ const char *library_path; ++ uint32_t flags; ++#define FPGAD_DEV_DETECTED 0x00000001 ++#define FPGAD_DEV_LOADED 0x00000002 ++ void *dl_handle; ++ const char *config; ++} fpgad_supported_device; ++ ++typedef enum _fpgad_plugin_type { ++ FPGAD_PLUGIN_TYPE_CALLBACK = 1, ++ FPGAD_PLUGIN_TYPE_THREAD ++} fpgad_plugin_type; ++ ++struct _fpgad_monitored_device; ++ ++typedef enum _fpgad_detection_status { ++ FPGAD_STATUS_NOT_DETECTED = 0, // no detection ++ FPGAD_STATUS_DETECTED, // detected (normal priority) ++ FPGAD_STATUS_DETECTED_HIGH // detected (high priority) ++} fpgad_detection_status; ++ ++typedef fpgad_detection_status ++ (*fpgad_detect_event_t)(struct _fpgad_monitored_device *dev, ++ void *context); ++typedef void (*fpgad_respond_event_t)(struct _fpgad_monitored_device *dev, ++ void *context); ++ ++typedef void * (*fpgad_plugin_thread_t)(void *context); ++typedef void (*fpgad_plugin_thread_stop_t)(void); ++ ++typedef struct _fpgad_monitored_device { ++ struct fpgad_config *config; ++ fpgad_supported_device *supported; ++ fpga_token token; ++ uint64_t object_id; ++ fpga_objtype object_type; ++ opae_bitstream_info *bitstr; ++ ++ fpgad_plugin_type type; ++ ++ // for type FPGAD_PLUGIN_TYPE_CALLBACK { ++ ++ // must be NULL-terminated ++ fpgad_detect_event_t *detections; ++ void **detection_contexts; ++ ++ fpgad_respond_event_t *responses; ++ void **response_contexts; ++ ++ // } ++ ++ // for type FPGAD_PLUGIN_TYPE_THREAD { ++ ++ fpgad_plugin_thread_t thread_fn; ++ ++ // The parameter to thread_fn will be the address ++ // of this fpgad_monitored_device. Use the ++ // following member to pass a thread-specific ++ // context: ++ ++ void *thread_context; ++ ++ // This routine is called to make the plugin ++ // thread stop execution in preparation for ++ // being joined. ++ fpgad_plugin_thread_stop_t thread_stop_fn; ++ ++ pthread_t thread; ++ ++ // } ++ ++#define MAX_DEV_ERROR_OCCURRENCES 64 ++ void *error_occurrences[MAX_DEV_ERROR_OCCURRENCES]; ++ unsigned num_error_occurrences; ++ ++#define MAX_DEV_SCRATCHPAD 2 ++ uint64_t scratchpad[MAX_DEV_SCRATCHPAD]; ++ ++ struct _fpgad_monitored_device *next; ++} fpgad_monitored_device; ++ ++#define FPGAD_PLUGIN_CONFIGURE "fpgad_plugin_configure" ++typedef int (*fpgad_plugin_configure_t)(fpgad_monitored_device *d, ++ const char *cfg); ++ ++#define FPGAD_PLUGIN_DESTROY "fpgad_plugin_destroy" ++typedef void (*fpgad_plugin_destroy_t)(fpgad_monitored_device *d); ++ ++#endif /* __FPGAD_MONITORED_DEVICE_H__ */ +diff --git a/tools/fpgad/plugins/fpgad-vc/CMakeLists.txt b/tools/fpgad/plugins/fpgad-vc/CMakeLists.txt +new file mode 100644 +index 00000000..fd43b5bd +--- /dev/null ++++ b/tools/fpgad/plugins/fpgad-vc/CMakeLists.txt +@@ -0,0 +1,41 @@ ++## Copyright(c) 2019-2020, Intel Corporation ++## ++## Redistribution and use in source and binary forms, with or without ++## modification, are permitted provided that the following conditions are met: ++## ++## * Redistributions of source code must retain the above copyright notice, ++## this list of conditions and the following disclaimer. ++## * Redistributions in binary form must reproduce the above copyright notice, ++## this list of conditions and the following disclaimer in the documentation ++## and/or other materials provided with the distribution. ++## * Neither the name of Intel Corporation nor the names of its contributors ++## may be used to endorse or promote products derived from this software ++## without specific prior written permission. ++## ++## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++## POSSIBILITY OF SUCH DAMAGE. ++ ++opae_add_module_library(TARGET fpgad-vc ++ SOURCE fpgad-vc.c ++ LIBS ++ opae-c ++ fpgad-api ++ ${libjson-c_LIBRARIES} ++ COMPONENT toolfpgad_vc ++) ++ ++target_include_directories(fpgad-vc ++ PRIVATE ++ ${OPAE_SDK_SOURCE}/tools ++ ${OPAE_LIBS_ROOT}/libopae-c ++ ${OPAE_LIBS_ROOT}/libbitstream ++) +diff --git a/tools/fpgad/plugins/fpgad-vc/fpgad-vc.c b/tools/fpgad/plugins/fpgad-vc/fpgad-vc.c +new file mode 100644 +index 00000000..c05b626d +--- /dev/null ++++ b/tools/fpgad/plugins/fpgad-vc/fpgad-vc.c +@@ -0,0 +1,1085 @@ ++// Copyright(c) 2019-2020, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include ++#include ++ ++#include "fpgad/api/opae_events_api.h" ++#include "fpgad/api/device_monitoring.h" ++#include "fpgad/api/sysfs.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("fpgad-vc: " format, ##__VA_ARGS__) ++ ++#define SYSFS_PATH_MAX 256 ++ ++#define TRIM_LOG_MODULUS 20 ++#define LOG_MOD(__r, __fmt, ...) \ ++do { \ ++\ ++ ++(__r); \ ++ if (!((__r) % TRIM_LOG_MODULUS)) { \ ++ log_printf("fpgad-vc: " __fmt, ##__VA_ARGS__); \ ++ } \ ++} while (0) ++ ++ ++typedef struct _vc_sensor { ++ fpga_object sensor_object; ++ char *name; ++ char *type; ++ uint64_t id; ++ fpga_object value_object; ++ uint64_t value; ++ uint64_t high_fatal; ++ uint64_t high_warn; ++ uint64_t low_fatal; ++ uint64_t low_warn; ++ uint32_t flags; ++#define FPGAD_SENSOR_VC_IGNORE 0x00000001 ++#define FPGAD_SENSOR_VC_HIGH_FATAL_VALID 0x00000002 ++#define FPGAD_SENSOR_VC_HIGH_WARN_VALID 0x00000004 ++#define FPGAD_SENSOR_VC_LOW_FATAL_VALID 0x00000008 ++#define FPGAD_SENSOR_VC_LOW_WARN_VALID 0x00000010 ++ uint32_t read_errors; ++#define FPGAD_SENSOR_VC_MAX_READ_ERRORS 25 ++} vc_sensor; ++ ++typedef struct _vc_config_sensor { ++ uint64_t id; ++ uint64_t high_fatal; ++ uint64_t high_warn; ++ uint64_t low_fatal; ++ uint64_t low_warn; ++ uint32_t flags; ++} vc_config_sensor; ++ ++#define MAX_VC_SENSORS 128 ++typedef struct _vc_device { ++ fpgad_monitored_device *base_device; ++ fpga_object group_object; ++ vc_sensor sensors[MAX_VC_SENSORS]; ++ uint32_t num_sensors; ++ uint64_t max_sensor_id; ++ uint8_t *state_tripped; // bit set ++ uint8_t *state_last; // bit set ++ uint64_t tripped_count; ++ uint32_t num_config_sensors; ++ vc_config_sensor *config_sensors; ++ bool aer_disabled; ++ uint32_t previous_ecap_aer[2]; ++} vc_device; ++ ++#define BIT_SET_MASK(__n) (1 << ((__n) % 8)) ++#define BIT_SET_INDEX(__n) ((__n) / 8) ++ ++#define BIT_SET_SET(__s, __n) \ ++((__s)[BIT_SET_INDEX(__n)] |= BIT_SET_MASK(__n)) ++ ++#define BIT_SET_CLR(__s, __n) \ ++((__s)[BIT_SET_INDEX(__n)] &= ~BIT_SET_MASK(__n)) ++ ++#define BIT_IS_SET(__s, __n) \ ++((__s)[BIT_SET_INDEX(__n)] & BIT_SET_MASK(__n)) ++ ++ ++#define HIGH_FATAL(__sens) \ ++(((__sens)->flags & FPGAD_SENSOR_VC_HIGH_FATAL_VALID) && \ ++ ((__sens)->value > (__sens)->high_fatal)) ++ ++#define HIGH_WARN(__sens) \ ++(((__sens)->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) && \ ++ ((__sens)->value > (__sens)->high_warn)) ++ ++#define HIGH_NORMAL(__sens) \ ++(((__sens)->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) && \ ++ ((__sens)->value <= (__sens)->high_warn)) ++ ++#define LOW_FATAL(__sens) \ ++(((__sens)->flags & FPGAD_SENSOR_VC_LOW_FATAL_VALID) && \ ++ ((__sens)->value < (__sens)->low_fatal)) ++ ++#define LOW_WARN(__sens) \ ++(((__sens)->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) && \ ++ ((__sens)->value < (__sens)->low_warn)) ++ ++#define LOW_NORMAL(__sens) \ ++(((__sens)->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) && \ ++ ((__sens)->value >= (__sens)->low_warn)) ++ ++STATIC bool vc_threads_running = true; ++ ++STATIC void stop_vc_threads(void) ++{ ++ vc_threads_running = false; ++} ++ ++STATIC void vc_destroy_sensor(vc_sensor *sensor) ++{ ++ fpgaDestroyObject(&sensor->sensor_object); ++ if (sensor->name) { ++ free(sensor->name); ++ sensor->name = NULL; ++ } ++ if (sensor->type) { ++ free(sensor->type); ++ sensor->type = NULL; ++ } ++ if (sensor->value_object) { ++ fpgaDestroyObject(&sensor->value_object); ++ sensor->value_object = NULL; ++ } ++ sensor->flags = 0; ++ sensor->read_errors = 0; ++} ++ ++STATIC void vc_destroy_sensors(vc_device *vc) ++{ ++ uint32_t i; ++ ++ for (i = 0 ; i < vc->num_sensors ; ++i) { ++ vc_destroy_sensor(&vc->sensors[i]); ++ } ++ vc->num_sensors = 0; ++ vc->max_sensor_id = 0; ++ ++ if (vc->group_object) { ++ fpgaDestroyObject(&vc->group_object); ++ vc->group_object = NULL; ++ } ++ ++ if (vc->state_tripped) { ++ free(vc->state_tripped); ++ vc->state_tripped = NULL; ++ } ++ ++ if (vc->state_last) { ++ free(vc->state_last); ++ vc->state_last = NULL; ++ } ++} ++ ++STATIC void vc_destroy_device(vc_device *vc) ++{ ++ vc_destroy_sensors(vc); ++ if (vc->config_sensors) { ++ free(vc->config_sensors); ++ vc->config_sensors = NULL; ++ vc->num_config_sensors = 0; ++ } ++} ++ ++STATIC fpga_result vc_sensor_get_string(vc_sensor *sensor, ++ const char *obj_name, ++ char **name) ++{ ++ fpga_result res; ++ fpga_object obj = NULL; ++ char buf[SYSFS_PATH_MAX] = { 0, }; ++ uint32_t len = 0; ++ ++ res = fpgaObjectGetObject(sensor->sensor_object, ++ obj_name, ++ &obj, ++ 0); ++ if (res != FPGA_OK) ++ return res; ++ ++ res = fpgaObjectGetSize(obj, &len, 0); ++ if (res != FPGA_OK) ++ goto out_free_obj; ++ ++ res = fpgaObjectRead(obj, (uint8_t *)buf, 0, len, 0); ++ if (res != FPGA_OK) ++ goto out_free_obj; ++ ++ if (buf[len-1] == '\n') ++ buf[len-1] = '\0'; ++ ++ *name = cstr_dup((const char *)buf); ++ if (!(*name)) ++ res = FPGA_NO_MEMORY; ++ ++out_free_obj: ++ fpgaDestroyObject(&obj); ++ return res; ++} ++ ++STATIC fpga_result vc_sensor_get_u64(vc_sensor *sensor, ++ const char *obj_name, ++ uint64_t *value) ++{ ++ fpga_result res; ++ fpga_object obj = NULL; ++ ++ res = fpgaObjectGetObject(sensor->sensor_object, ++ obj_name, ++ &obj, ++ 0); ++ if (res != FPGA_OK) ++ return res; ++ ++ res = fpgaObjectRead64(obj, value, 0); ++ ++ fpgaDestroyObject(&obj); ++ ++ return res; ++} ++ ++// The percentage by which we adjust the power trip ++// points so that we catch anomolies before the hw does. ++#define VC_PERCENT_ADJUST_PWR 5 ++// The number of degrees by which we adjust the ++// temperature trip points so that we catch anomolies ++// before the hw does. ++#define VC_DEGREES_ADJUST_TEMP 5 ++STATIC fpga_result vc_sensor_get(vc_device *vc, vc_sensor *s) ++{ ++ fpga_result res; ++ bool is_temperature; ++ int indicator = -1; ++ vc_config_sensor *cfg_sensor = NULL; ++ uint32_t i; ++ ++ if (s->name) { ++ free(s->name); ++ s->name = NULL; ++ } ++ ++ if (s->type) { ++ free(s->type); ++ s->type = NULL; ++ } ++ ++ res = vc_sensor_get_string(s, "name", &s->name); ++ if (res != FPGA_OK) ++ return res; ++ ++ res = vc_sensor_get_string(s, "type", &s->type); ++ if (res != FPGA_OK) ++ return res; ++ ++ res = vc_sensor_get_u64(s, "id", &s->id); ++ if (res != FPGA_OK) ++ return res; ++ ++ res = fpgaObjectRead64(s->value_object, &s->value, 0); ++ if (res != FPGA_OK) ++ return res; ++ ++ indicator = strcmp(s->type, "Temperature"); ++ is_temperature = (indicator == 0); ++ ++ res = vc_sensor_get_u64(s, "high_fatal", &s->high_fatal); ++ if (res == FPGA_OK) { ++ s->flags |= FPGAD_SENSOR_VC_HIGH_FATAL_VALID; ++ if (is_temperature) ++ s->high_fatal -= VC_DEGREES_ADJUST_TEMP; ++ else ++ s->high_fatal -= ++ (s->high_fatal * VC_PERCENT_ADJUST_PWR) / 100; ++ } else ++ s->flags &= ~FPGAD_SENSOR_VC_HIGH_FATAL_VALID; ++ ++ res = vc_sensor_get_u64(s, "high_warn", &s->high_warn); ++ if (res == FPGA_OK) ++ s->flags |= FPGAD_SENSOR_VC_HIGH_WARN_VALID; ++ else ++ s->flags &= ~FPGAD_SENSOR_VC_HIGH_WARN_VALID; ++ ++ res = vc_sensor_get_u64(s, "low_fatal", &s->low_fatal); ++ if (res == FPGA_OK) { ++ s->flags |= FPGAD_SENSOR_VC_LOW_FATAL_VALID; ++ if (is_temperature) ++ s->low_fatal += VC_DEGREES_ADJUST_TEMP; ++ else ++ s->low_fatal += ++ (s->low_fatal * VC_PERCENT_ADJUST_PWR) / 100; ++ } else ++ s->flags &= ~FPGAD_SENSOR_VC_LOW_FATAL_VALID; ++ ++ res = vc_sensor_get_u64(s, "low_warn", &s->low_warn); ++ if (res == FPGA_OK) ++ s->flags |= FPGAD_SENSOR_VC_LOW_WARN_VALID; ++ else ++ s->flags &= ~FPGAD_SENSOR_VC_LOW_WARN_VALID; ++ ++ /* Do we have a user override (via the config) for ++ * this sensor? If so, then honor it. ++ */ ++ for (i = 0 ; i < vc->num_config_sensors ; ++i) { ++ if (vc->config_sensors[i].flags & ++ FPGAD_SENSOR_VC_IGNORE) ++ continue; ++ if (vc->config_sensors[i].id == s->id) { ++ cfg_sensor = &vc->config_sensors[i]; ++ break; ++ } ++ } ++ ++ if (cfg_sensor) { ++ ++ if (cfg_sensor->flags & FPGAD_SENSOR_VC_HIGH_FATAL_VALID) { ++ // Cap the sensor at the adjusted max ++ // allowed by the hardware. ++ if ((s->flags & FPGAD_SENSOR_VC_HIGH_FATAL_VALID) && ++ (cfg_sensor->high_fatal > s->high_fatal)) ++ /* nothing */ ; ++ else ++ s->high_fatal = cfg_sensor->high_fatal; ++ ++ s->flags |= FPGAD_SENSOR_VC_HIGH_FATAL_VALID; ++ } else ++ s->flags &= ~FPGAD_SENSOR_VC_HIGH_FATAL_VALID; ++ ++ if (cfg_sensor->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) { ++ ++ if ((s->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) && ++ (cfg_sensor->high_warn > s->high_warn)) ++ /* nothing */ ; ++ else ++ s->high_warn = cfg_sensor->high_warn; ++ ++ s->flags |= FPGAD_SENSOR_VC_HIGH_WARN_VALID; ++ } else ++ s->flags &= ~FPGAD_SENSOR_VC_HIGH_WARN_VALID; ++ ++ if (cfg_sensor->flags & FPGAD_SENSOR_VC_LOW_FATAL_VALID) { ++ ++ if ((s->flags & FPGAD_SENSOR_VC_LOW_FATAL_VALID) && ++ (cfg_sensor->low_fatal < s->low_fatal)) ++ /* nothing */ ; ++ else ++ s->low_fatal = cfg_sensor->low_fatal; ++ ++ s->flags |= FPGAD_SENSOR_VC_LOW_FATAL_VALID; ++ } else ++ s->flags &= ~FPGAD_SENSOR_VC_LOW_FATAL_VALID; ++ ++ if (cfg_sensor->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) { ++ ++ if ((s->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) && ++ (cfg_sensor->low_warn < s->low_warn)) ++ /* nothing */ ; ++ else ++ s->low_warn = cfg_sensor->low_warn; ++ ++ s->flags |= FPGAD_SENSOR_VC_LOW_WARN_VALID; ++ } else ++ s->flags &= ~FPGAD_SENSOR_VC_LOW_WARN_VALID; ++ ++ } ++ ++ return FPGA_OK; ++} ++ ++STATIC fpga_result vc_enum_sensor(vc_device *vc, ++ const char *name) ++{ ++ fpga_result res; ++ vc_sensor *s; ++ ++ if (vc->num_sensors == MAX_VC_SENSORS) { ++ LOG("exceeded max number of sensors.\n"); ++ return FPGA_EXCEPTION; ++ } ++ ++ s = &vc->sensors[vc->num_sensors]; ++ ++ res = fpgaObjectGetObject(vc->group_object, ++ name, ++ &s->sensor_object, ++ 0); ++ ++ if (res != FPGA_OK) ++ return res; ++ ++ res = fpgaObjectGetObject(s->sensor_object, ++ "value", ++ &s->value_object, ++ 0); ++ ++ if (res != FPGA_OK) { ++ LOG("failed to get value object for %s.\n", name); ++ fpgaDestroyObject(&s->sensor_object); ++ return res; ++ } ++ ++ res = vc_sensor_get(vc, s); ++ if (res == FPGA_OK) ++ ++vc->num_sensors; ++ else { ++ LOG("warning: sensor attribute enumeration failed.\n"); ++ vc_destroy_sensor(s); ++ } ++ ++ return res; ++} ++ ++STATIC fpga_result vc_enum_sensors(vc_device *vc) ++{ ++ fpga_result res; ++ char name[SYSFS_PATH_MAX]; ++ int i; ++ ++ res = fpgaTokenGetObject(vc->base_device->token, ++ "spi-altera.*.auto/spi_master/spi*/spi*.*", ++ &vc->group_object, ++ FPGA_OBJECT_GLOB); ++ if (res) ++ return res; ++ ++ for (i = 0 ; i < MAX_VC_SENSORS ; ++i) { ++ snprintf(name, sizeof(name), "sensor%d", i); ++ vc_enum_sensor(vc, name); ++ } ++ ++ if (vc->num_sensors > 0) { ++ vc_sensor *s = &vc->sensors[vc->num_sensors - 1]; ++ ++ vc->max_sensor_id = s->id; ++ ++ vc->state_tripped = calloc((vc->max_sensor_id + 7) / 8, 1); ++ vc->state_last = calloc((vc->max_sensor_id + 7) / 8, 1); ++ ++ return (vc->state_tripped && vc->state_last) ? ++ FPGA_OK : FPGA_NO_MEMORY; ++ } ++ ++ return FPGA_NOT_FOUND; ++} ++ ++STATIC fpga_result vc_disable_aer(vc_device *vc) ++{ ++ fpga_token token; ++ fpga_result res; ++ fpga_properties prop = NULL; ++ char path[PATH_MAX]; ++ char rlpath[PATH_MAX]; ++ char *p; ++ char cmd[256]; ++ char output[256]; ++ FILE *fp; ++ size_t sz; ++ ++ uint16_t seg = 0; ++ uint8_t bus = 0; ++ uint8_t dev = 0; ++ uint8_t fn = 0; ++ ++ token = vc->base_device->token; ++ ++ res = fpgaGetProperties(token, &prop); ++ if (res != FPGA_OK) { ++ LOG("failed to get fpga properties.\n"); ++ return res; ++ } ++ ++ if ((fpgaPropertiesGetSegment(prop, &seg) != FPGA_OK) || ++ (fpgaPropertiesGetBus(prop, &bus) != FPGA_OK) || ++ (fpgaPropertiesGetDevice(prop, &dev) != FPGA_OK) || ++ (fpgaPropertiesGetFunction(prop, &fn) != FPGA_OK)) { ++ LOG("failed to get PCI attributes.\n"); ++ fpgaDestroyProperties(&prop); ++ return FPGA_EXCEPTION; ++ } ++ ++ fpgaDestroyProperties(&prop); ++ ++ snprintf(path, sizeof(path), ++ "/sys/bus/pci/devices/%04x:%02x:%02x.%d", ++ (int)seg, (int)bus, (int)dev, (int)fn); ++ ++ memset(rlpath, 0, sizeof(rlpath)); ++ ++ if (readlink(path, rlpath, sizeof(rlpath)) < 0) { ++ LOG("readlink \"%s\" failed.\n", path); ++ return FPGA_EXCEPTION; ++ } ++ ++ // (rlpath) ++ // 1111111111 ++ // 01234567890123456789 ++ // ../../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.0/ ++ // 0000:b0:09.0/0000:b2:00.0 ++ ++ p = strstr(rlpath, "devices/pci"); ++ ++ p += 19; ++ *(p + 12) = '\0'; ++ ++ // Save the current ECAP_AER values. ++ ++ snprintf(cmd, sizeof(cmd), ++ "setpci -s %s ECAP_AER+0x08.L", p); ++ ++ fp = popen(cmd, "r"); ++ if (!fp) { ++ LOG("failed to read ECAP_AER+0x08 for %s\n", p); ++ return FPGA_EXCEPTION; ++ } ++ ++ sz = fread(output, 1, sizeof(output), fp); ++ ++ if (sz >= sizeof(output)) ++ sz = sizeof(output) - 1; ++ output[sz] = '\0'; ++ ++ pclose(fp); ++ ++ vc->previous_ecap_aer[0] = strtoul(output, NULL, 16); ++ ++ LOG("saving previous ECAP_AER+0x08 value 0x%08x for %s\n", ++ vc->previous_ecap_aer[0], p); ++ ++ ++ snprintf(cmd, sizeof(cmd), ++ "setpci -s %s ECAP_AER+0x14.L", p); ++ ++ fp = popen(cmd, "r"); ++ if (!fp) { ++ LOG("failed to read ECAP_AER+0x14 for %s\n", p); ++ return FPGA_EXCEPTION; ++ } ++ ++ sz = fread(output, 1, sizeof(output), fp); ++ ++ if (sz >= sizeof(output)) ++ sz = sizeof(output) - 1; ++ output[sz] = '\0'; ++ ++ pclose(fp); ++ ++ vc->previous_ecap_aer[1] = strtoul(output, NULL, 16); ++ ++ LOG("saving previous ECAP_AER+0x14 value 0x%08x for %s\n", ++ vc->previous_ecap_aer[1], p); ++ ++ ++ // Disable AER. ++ ++ snprintf(cmd, sizeof(cmd), ++ "setpci -s %s ECAP_AER+0x08.L=0xffffffff", p); ++ ++ fp = popen(cmd, "r"); ++ if (!fp) { ++ LOG("failed to write ECAP_AER+0x08 for %s\n", p); ++ return FPGA_EXCEPTION; ++ } ++ ++ pclose(fp); ++ ++ snprintf(cmd, sizeof(cmd), ++ "setpci -s %s ECAP_AER+0x14.L=0xffffffff", p); ++ ++ fp = popen(cmd, "r"); ++ if (!fp) { ++ LOG("failed to write ECAP_AER+0x14 for %s\n", p); ++ return FPGA_EXCEPTION; ++ } ++ ++ pclose(fp); ++ ++ return FPGA_OK; ++} ++ ++STATIC fpga_result vc_enable_aer(vc_device *vc) ++{ ++ fpga_token token; ++ fpga_result res; ++ fpga_properties prop = NULL; ++ char path[PATH_MAX]; ++ char rlpath[PATH_MAX]; ++ char *p; ++ char cmd[256]; ++ FILE *fp; ++ ++ uint16_t seg = 0; ++ uint8_t bus = 0; ++ uint8_t dev = 0; ++ uint8_t fn = 0; ++ ++ token = vc->base_device->token; ++ ++ res = fpgaGetProperties(token, &prop); ++ if (res != FPGA_OK) { ++ LOG("failed to get fpga properties.\n"); ++ return res; ++ } ++ ++ if ((fpgaPropertiesGetSegment(prop, &seg) != FPGA_OK) || ++ (fpgaPropertiesGetBus(prop, &bus) != FPGA_OK) || ++ (fpgaPropertiesGetDevice(prop, &dev) != FPGA_OK) || ++ (fpgaPropertiesGetFunction(prop, &fn) != FPGA_OK)) { ++ LOG("failed to get PCI attributes.\n"); ++ fpgaDestroyProperties(&prop); ++ return FPGA_EXCEPTION; ++ } ++ ++ fpgaDestroyProperties(&prop); ++ ++ snprintf(path, sizeof(path), ++ "/sys/bus/pci/devices/%04x:%02x:%02x.%d", ++ (int)seg, (int)bus, (int)dev, (int)fn); ++ ++ memset(rlpath, 0, sizeof(rlpath)); ++ ++ if (readlink(path, rlpath, sizeof(rlpath)) < 0) { ++ LOG("readlink \"%s\" failed.\n", path); ++ return FPGA_EXCEPTION; ++ } ++ ++ // (rlpath) ++ // 1111111111 ++ // 01234567890123456789 ++ // ../../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.0/ ++ // 0000:b0:09.0/0000:b2:00.0 ++ ++ p = strstr(rlpath, "devices/pci"); ++ ++ p += 19; ++ *(p + 12) = '\0'; ++ ++ // Write the saved ECAP_AER values to enable AER. ++ ++ snprintf(cmd, sizeof(cmd), ++ "setpci -s %s ECAP_AER+0x08.L=0x%08x", ++ p, vc->previous_ecap_aer[0]); ++ ++ fp = popen(cmd, "r"); ++ if (!fp) { ++ LOG("failed to write ECAP_AER+0x08 for %s\n", p); ++ return FPGA_EXCEPTION; ++ } ++ ++ pclose(fp); ++ ++ LOG("restored previous ECAP_AER+0x08 value 0x%08x for %s\n", ++ vc->previous_ecap_aer[0], p); ++ ++ ++ snprintf(cmd, sizeof(cmd), ++ "setpci -s %s ECAP_AER+0x14.L=0x%08x", ++ p, vc->previous_ecap_aer[1]); ++ ++ fp = popen(cmd, "r"); ++ if (!fp) { ++ LOG("failed to write ECAP_AER+0x14 for %s\n", p); ++ return FPGA_EXCEPTION; ++ } ++ ++ pclose(fp); ++ ++ LOG("restored previous ECAP_AER+0x14 value 0x%08x for %s\n", ++ vc->previous_ecap_aer[1], p); ++ ++ return FPGA_OK; ++} ++ ++STATIC bool vc_monitor_sensors(vc_device *vc) ++{ ++ uint32_t i; ++ uint32_t monitoring = 0; ++ bool negative_trans = false; ++ bool res = true; ++ ++ for (i = 0 ; i < vc->num_sensors ; ++i) { ++ vc_sensor *s = &vc->sensors[i]; ++ ++ if (s->flags & FPGAD_SENSOR_VC_IGNORE) ++ continue; ++ ++ if (s->flags & (FPGAD_SENSOR_VC_HIGH_WARN_VALID| ++ FPGAD_SENSOR_VC_LOW_WARN_VALID)) ++ ++monitoring; ++ ++ if (fpgaObjectRead64(s->value_object, ++ &s->value, ++ FPGA_OBJECT_SYNC) != FPGA_OK) { ++ if (++s->read_errors >= ++ FPGAD_SENSOR_VC_MAX_READ_ERRORS) ++ s->flags |= FPGAD_SENSOR_VC_IGNORE; ++ continue; ++ } ++ ++ if (HIGH_WARN(s) || LOW_WARN(s)) { ++ opae_api_send_EVENT_POWER_THERMAL(vc->base_device); ++ BIT_SET_SET(vc->state_tripped, s->id); ++ if (!BIT_IS_SET(vc->state_last, s->id)) { ++ LOG("sensor '%s' warning.\n", s->name); ++ if (!vc->aer_disabled) { ++ if (FPGA_OK == vc_disable_aer(vc)) ++ vc->aer_disabled = true; ++ } ++ } ++ } ++ ++ if (HIGH_NORMAL(s) || LOW_NORMAL(s)) { ++ if (BIT_IS_SET(vc->state_last, s->id)) { ++ negative_trans = true; ++ LOG("sensor '%s' back to normal.\n", s->name); ++ } ++ } ++ ++ if (BIT_IS_SET(vc->state_last, s->id) && ++ BIT_IS_SET(vc->state_tripped, s->id)) { ++ LOG_MOD(vc->tripped_count, ++ "sensor '%s' still tripped.\n", s->name); ++ } ++ } ++ ++ if (negative_trans) { ++ for (i = 0 ; i < vc->num_sensors ; ++i) { ++ if (BIT_IS_SET(vc->state_tripped, vc->sensors[i].id)) ++ break; ++ } ++ ++ if (i == vc->num_sensors) { ++ // no remaining tripped sensors ++ vc->tripped_count = 0; ++ if (vc->aer_disabled) { ++ if (FPGA_OK == vc_enable_aer(vc)) ++ vc->aer_disabled = false; ++ } ++ } ++ } ++ ++ /* ++ ** Are we still monitoring any sensor that has a valid ++ ** high/low warn threshold? If not, then this fn should ++ ** return false so that we wait for sensor enumeration ++ ** in the caller. It's possible that we're ignoring all ++ ** of the sensors because they became unavailable due to ++ ** driver removal, eg. ++ */ ++ if (!monitoring) ++ res = false; ++ ++ for (i = 0 ; i < vc->num_sensors ; ++i) { ++ vc_sensor *s = &vc->sensors[i]; ++ if (BIT_IS_SET(vc->state_tripped, s->id)) ++ BIT_SET_SET(vc->state_last, s->id); ++ else ++ BIT_SET_CLR(vc->state_last, s->id); ++ } ++ ++ memset(vc->state_tripped, 0, (vc->max_sensor_id + 7) / 8); ++ ++ return res; ++} ++ ++STATIC int cool_down = 30; ++ ++STATIC void *monitor_fme_vc_thread(void *arg) ++{ ++ fpgad_monitored_device *d = ++ (fpgad_monitored_device *)arg; ++ ++ vc_device *vc = (vc_device *)d->thread_context; ++ ++ uint32_t enum_retries = 0; ++ uint8_t *save_state_last = NULL; ++ ++ while (vc_threads_running) { ++ ++ while (vc_enum_sensors(vc) != FPGA_OK) { ++ LOG_MOD(enum_retries, "waiting to enumerate sensors.\n"); ++ if (!vc_threads_running) ++ return NULL; ++ sleep(1); ++ } ++ ++ if (save_state_last) { ++ free(vc->state_last); ++ vc->state_last = save_state_last; ++ save_state_last = NULL; ++ } ++ ++ while (vc_monitor_sensors(vc)) { ++ usleep(d->config->poll_interval_usec); ++ ++ if (!vc_threads_running) { ++ vc_destroy_sensors(vc); ++ return NULL; ++ } ++ } ++ ++ save_state_last = vc->state_last; ++ vc->state_last = NULL; ++ ++ vc_destroy_sensors(vc); ++ ++ } ++ ++ return NULL; ++} ++ ++STATIC int vc_parse_config(vc_device *vc, const char *cfg) ++{ ++ json_object *root; ++ enum json_tokener_error j_err = json_tokener_success; ++ json_object *j_cool_down = NULL; ++ json_object *j_config_sensors_enabled = NULL; ++ json_object *j_sensors = NULL; ++ int res = 1; ++ int sensor_entries; ++ int i; ++ ++ root = json_tokener_parse_verbose(cfg, &j_err); ++ if (!root) { ++ LOG("error parsing config: %s\n", ++ json_tokener_error_desc(j_err)); ++ return 1; ++ } ++ ++ if (!json_object_object_get_ex(root, ++ "cool-down", ++ &j_cool_down)) { ++ LOG("failed to find cool-down key in config.\n"); ++ goto out_put; ++ } ++ ++ if (!json_object_is_type(j_cool_down, json_type_int)) { ++ LOG("cool-down key not integer.\n"); ++ goto out_put; ++ } ++ ++ cool_down = json_object_get_int(j_cool_down); ++ if (cool_down < 0) ++ cool_down = 30; ++ ++ LOG("set cool-down period to %d seconds.\n", cool_down); ++ ++ res = 0; ++ ++ if (!json_object_object_get_ex(root, ++ "config-sensors-enabled", ++ &j_config_sensors_enabled)) { ++ LOG("failed to find config-sensors-enabled key in config.\n" ++ "Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ if (!json_object_is_type(j_config_sensors_enabled, json_type_boolean)) { ++ LOG("config-sensors-enabled key not Boolean.\n" ++ "Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ if (!json_object_get_boolean(j_config_sensors_enabled)) { ++ LOG("config-sensors-enabled key set to false.\n" ++ "Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ if (!json_object_object_get_ex(root, ++ "sensors", ++ &j_sensors)) { ++ LOG("failed to find sensors key in config.\n" ++ "Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ if (!json_object_is_type(j_sensors, json_type_array)) { ++ LOG("sensors key not array.\n" ++ "Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ sensor_entries = json_object_array_length(j_sensors); ++ if (!sensor_entries) { ++ LOG("sensors key is empty array.\n" ++ "Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ vc->config_sensors = calloc(sensor_entries, sizeof(vc_config_sensor)); ++ if (!vc->config_sensors) { ++ LOG("calloc failed. Skipping user sensor config.\n"); ++ goto out_put; ++ } ++ ++ vc->num_config_sensors = (uint32_t)sensor_entries; ++ ++ for (i = 0 ; i < sensor_entries ; ++i) { ++ json_object *j_sensor_sub_i = json_object_array_get_idx(j_sensors, i); ++ json_object *j_id; ++ json_object *j_high_fatal; ++ json_object *j_high_warn; ++ json_object *j_low_fatal; ++ json_object *j_low_warn; ++ ++ if (!json_object_object_get_ex(j_sensor_sub_i, ++ "id", ++ &j_id)) { ++ LOG("failed to find id key in config sensors[%d].\n" ++ "Skipping entry %d.\n", i, i); ++ vc->config_sensors[i].id = MAX_VC_SENSORS; ++ vc->config_sensors[i].flags = FPGAD_SENSOR_VC_IGNORE; ++ continue; ++ } ++ ++ if (!json_object_is_type(j_id, json_type_int)) { ++ LOG("sensors[%d].id key not int.\n" ++ "Skipping entry %d.\n", i, i); ++ vc->config_sensors[i].id = MAX_VC_SENSORS; ++ vc->config_sensors[i].flags = FPGAD_SENSOR_VC_IGNORE; ++ continue; ++ } ++ ++ vc->config_sensors[i].id = json_object_get_int(j_id); ++ ++ if (json_object_object_get_ex(j_sensor_sub_i, ++ "high-fatal", ++ &j_high_fatal)) { ++ if (json_object_is_type(j_high_fatal, ++ json_type_double)) { ++ vc->config_sensors[i].high_fatal = ++ (uint64_t)(json_object_get_double(j_high_fatal) ++ * 1000.0); ++ vc->config_sensors[i].flags |= ++ FPGAD_SENSOR_VC_HIGH_FATAL_VALID; ++ LOG("user sensor%u high-fatal: %f\n", ++ vc->config_sensors[i].id, ++ json_object_get_double(j_high_fatal)); ++ } ++ } ++ ++ if (json_object_object_get_ex(j_sensor_sub_i, ++ "high-warn", ++ &j_high_warn)) { ++ if (json_object_is_type(j_high_warn, ++ json_type_double)) { ++ vc->config_sensors[i].high_warn = ++ (uint64_t)(json_object_get_double(j_high_warn) ++ * 1000.0); ++ vc->config_sensors[i].flags |= ++ FPGAD_SENSOR_VC_HIGH_WARN_VALID; ++ LOG("user sensor%u high-warn: %f\n", ++ vc->config_sensors[i].id, ++ json_object_get_double(j_high_warn)); ++ } ++ } ++ ++ if (json_object_object_get_ex(j_sensor_sub_i, ++ "low-fatal", ++ &j_low_fatal)) { ++ if (json_object_is_type(j_low_fatal, ++ json_type_double)) { ++ vc->config_sensors[i].low_fatal = ++ (uint64_t)(json_object_get_double(j_low_fatal) ++ * 1000.0); ++ vc->config_sensors[i].flags |= ++ FPGAD_SENSOR_VC_LOW_FATAL_VALID; ++ LOG("user sensor%u low-fatal: %f\n", ++ vc->config_sensors[i].id, ++ json_object_get_double(j_low_fatal)); ++ } ++ } ++ ++ if (json_object_object_get_ex(j_sensor_sub_i, ++ "low-warn", ++ &j_low_warn)) { ++ if (json_object_is_type(j_low_warn, ++ json_type_double)) { ++ vc->config_sensors[i].low_warn = ++ (uint64_t)(json_object_get_double(j_low_warn) ++ * 1000.0); ++ vc->config_sensors[i].flags |= ++ FPGAD_SENSOR_VC_LOW_WARN_VALID; ++ LOG("user sensor%u low-warn: %f\n", ++ vc->config_sensors[i].id, ++ json_object_get_double(j_low_warn)); ++ } ++ } ++ } ++ ++out_put: ++ json_object_put(root); ++ return res; ++} ++ ++int fpgad_plugin_configure(fpgad_monitored_device *d, ++ const char *cfg) ++{ ++ int res = 1; ++ vc_device *vc; ++ ++ vc_threads_running = true; ++ ++ d->type = FPGAD_PLUGIN_TYPE_THREAD; ++ ++ if (d->object_type == FPGA_DEVICE) { ++ ++ d->thread_fn = monitor_fme_vc_thread; ++ d->thread_stop_fn = stop_vc_threads; ++ ++ vc = calloc(1, sizeof(vc_device)); ++ if (!vc) ++ return res; ++ ++ vc->base_device = d; ++ d->thread_context = vc; ++ ++ LOG("monitoring vid=0x%04x did=0x%04x (%s)\n", ++ d->supported->vendor_id, ++ d->supported->device_id, ++ d->object_type == FPGA_ACCELERATOR ? ++ "accelerator" : "device"); ++ ++ res = vc_parse_config(vc, cfg); ++ if (res) { ++ free(vc); ++ } ++ ++ } ++ ++ // Not currently monitoring the Port device ++ ++ return res; ++} ++ ++void fpgad_plugin_destroy(fpgad_monitored_device *d) ++{ ++ LOG("stop monitoring vid=0x%04x did=0x%04x (%s)\n", ++ d->supported->vendor_id, ++ d->supported->device_id, ++ d->object_type == FPGA_ACCELERATOR ? ++ "accelerator" : "device"); ++ ++ if (d->thread_context) { ++ vc_destroy_device((vc_device *)d->thread_context); ++ free(d->thread_context); ++ d->thread_context = NULL; ++ } ++} +diff --git a/tools/fpgad/plugins/fpgad-xfpga/CMakeLists.txt b/tools/fpgad/plugins/fpgad-xfpga/CMakeLists.txt +new file mode 100644 +index 00000000..fd8c13d0 +--- /dev/null ++++ b/tools/fpgad/plugins/fpgad-xfpga/CMakeLists.txt +@@ -0,0 +1,41 @@ ++## Copyright(c) 2018-2020, Intel Corporation ++## ++## Redistribution and use in source and binary forms, with or without ++## modification, are permitted provided that the following conditions are met: ++## ++## * Redistributions of source code must retain the above copyright notice, ++## this list of conditions and the following disclaimer. ++## * Redistributions in binary form must reproduce the above copyright notice, ++## this list of conditions and the following disclaimer in the documentation ++## and/or other materials provided with the distribution. ++## * Neither the name of Intel Corporation nor the names of its contributors ++## may be used to endorse or promote products derived from this software ++## without specific prior written permission. ++## ++## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++## POSSIBILITY OF SUCH DAMAGE. ++ ++opae_add_module_library(TARGET fpgad-xfpga ++ SOURCE fpgad-xfpga.c ++ LIBS ++ opae-c ++ fpgad-api ++ ${libjson-c_LIBRARIES} ++ COMPONENT toolfpgad_xfpga ++) ++ ++target_include_directories(fpgad-xfpga ++ PRIVATE ++ ${OPAE_SDK_SOURCE}/tools ++ ${OPAE_LIBS_ROOT}/libopae-c ++ ${OPAE_LIBS_ROOT}/libbitstream ++) +diff --git a/tools/fpgad/plugins/fpgad-xfpga/fpgad-xfpga.c b/tools/fpgad/plugins/fpgad-xfpga/fpgad-xfpga.c +new file mode 100644 +index 00000000..fa5846bb +--- /dev/null ++++ b/tools/fpgad/plugins/fpgad-xfpga/fpgad-xfpga.c +@@ -0,0 +1,992 @@ ++// Copyright(c) 2018-2019, Intel Corporation ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are met: ++// ++// * Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above copyright notice, ++// this list of conditions and the following disclaimer in the documentation ++// and/or other materials provided with the distribution. ++// * Neither the name of Intel Corporation nor the names of its contributors ++// may be used to endorse or promote products derived from this software ++// without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++// POSSIBILITY OF SUCH DAMAGE. ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif // HAVE_CONFIG_H ++ ++#include "fpgad/api/opae_events_api.h" ++#include "fpgad/api/device_monitoring.h" ++ ++#ifdef LOG ++#undef LOG ++#endif ++#define LOG(format, ...) \ ++log_printf("fpgad-xfpga: " format, ##__VA_ARGS__) ++ ++enum fpga_power_state { ++ FPGAD_NORMAL_PWR = 0, ++ FPGAD_AP1_STATE, ++ FPGAD_AP2_STATE, ++ FPGAD_AP6_STATE ++}; ++ ++typedef struct _fpgad_xfpga_AP_context { ++ const char *sysfs_file; ++ const char *message; ++ int low_bit; ++ int high_bit; ++} fpgad_xfpga_AP_context; ++ ++fpgad_xfpga_AP_context fpgad_xfpga_AP_contexts[] = { ++ { "ap1_event", "AP1 Triggered!", 0, 0 }, ++ { "ap2_event", "AP2 Triggered!", 0, 0 }, ++ { "power_state", "Power state changed to", 0, 1 }, ++}; ++ ++fpgad_detection_status ++fpgad_xfpga_detect_AP1_or_AP2(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_AP_context *c = ++ (fpgad_xfpga_AP_context *)context; ++ fpga_object obj = NULL; ++ fpga_result res; ++ uint64_t err = 0; ++ uint64_t mask; ++ uint64_t value; ++ int i; ++ bool detected = false; ++ ++ res = fpgaTokenGetObject(d->token, c->sysfs_file, ++ &obj, 0); ++ if (res != FPGA_OK) { ++ LOG("failed to get error object\n"); ++ return FPGAD_STATUS_NOT_DETECTED; ++ } ++ ++ res = fpgaObjectRead64(obj, &err, 0); ++ if (res != FPGA_OK) { ++ LOG("failed to read error object\n"); ++ fpgaDestroyObject(&obj); ++ return FPGAD_STATUS_NOT_DETECTED; ++ } ++ ++ fpgaDestroyObject(&obj); ++ ++ mask = 0; ++ for (i = c->low_bit ; i <= c->high_bit ; ++i) ++ mask |= 1ULL << i; ++ ++ value = mask & err; ++ ++ if (value != 0 && !mon_has_error_occurred(d, context)) { ++ detected = mon_add_device_error(d, context); ++ } ++ ++ if (value == 0 && mon_has_error_occurred(d, context)) { ++ mon_remove_device_error(d, context); ++ } ++ ++ return detected ? FPGAD_STATUS_DETECTED : FPGAD_STATUS_NOT_DETECTED; ++} ++ ++void fpgad_xfpga_respond_AP1_or_AP2(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_AP_context *c = ++ (fpgad_xfpga_AP_context *)context; ++ ++ LOG("%s\n", c->message); ++ ++ // Signal OPAE events API ++ opae_api_send_EVENT_POWER_THERMAL(d); ++} ++ ++fpgad_detection_status ++fpgad_xfpga_detect_PowerStateChange(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_AP_context *c = ++ (fpgad_xfpga_AP_context *)context; ++ fpga_object obj = NULL; ++ fpga_result res; ++ uint64_t err = 0; ++ uint64_t mask; ++ uint64_t value; ++ int i; ++ bool detected = false; ++ ++ res = fpgaTokenGetObject(d->token, c->sysfs_file, ++ &obj, 0); ++ if (res != FPGA_OK) { ++ LOG("failed to get error object\n"); ++ return FPGAD_STATUS_NOT_DETECTED; ++ } ++ ++ res = fpgaObjectRead64(obj, &err, 0); ++ if (res != FPGA_OK) { ++ LOG("failed to read error object\n"); ++ fpgaDestroyObject(&obj); ++ return FPGAD_STATUS_NOT_DETECTED; ++ } ++ ++ fpgaDestroyObject(&obj); ++ ++ mask = 0; ++ for (i = c->low_bit ; i <= c->high_bit ; ++i) ++ mask |= 1ULL << i; ++ ++ value = mask & err; ++ ++ if (value != d->scratchpad[0]) { ++ detected = true; ++ } ++ ++ d->scratchpad[0] = value; ++ ++ return detected ? FPGAD_STATUS_DETECTED : FPGAD_STATUS_NOT_DETECTED; ++} ++ ++void fpgad_xfpga_respond_PowerStateChange(fpgad_monitored_device *d, ++ void *context) ++{ ++ const char *power_states[] = { ++ "Normal Power", ++ "AP1 Power State", ++ "AP2 Power State", ++ "AP6 Power State" ++ }; ++ ++ fpgad_xfpga_AP_context *c = ++ (fpgad_xfpga_AP_context *)context; ++ ++ LOG("%s %s\n", c->message, ++ d->scratchpad[0] < 4 ? ++ power_states[d->scratchpad[0]] : ++ "unknown"); ++} ++ ++typedef struct _fpgad_xfpga_Error_context { ++ const char *sysfs_file; ++ const char *message; ++ int low_bit; ++ int high_bit; ++} fpgad_xfpga_Error_context; ++ ++fpgad_xfpga_Error_context fpgad_xfpga_Error_contexts[] = { ++ /* 0 */ { "errors/errors", "PORT_ERROR[0x1010].VfFlrAccessError", 51, 51 }, ++ /* 1 */ { "errors/errors", "PORT_ERROR[0x1010].Ap6Event", 50, 50 }, /* AP6 NULL GBS */ ++ /* 2 */ { "errors/errors", "PORT_ERROR[0x1010].PMRError", 49, 49 }, ++ /* 3 */ { "errors/errors", "PORT_ERROR[0x1010].PageFault", 48, 48 }, ++ /* 4 */ { "errors/errors", "PORT_ERROR[0x1010].VgaMemRangeError", 47, 47 }, ++ /* 5 */ { "errors/errors", "PORT_ERROR[0x1010].LegRangeHighError", 46, 46 }, ++ /* 6 */ { "errors/errors", "PORT_ERROR[0x1010].LegRangeLowError", 45, 45 }, ++ /* 7 */ { "errors/errors", "PORT_ERROR[0x1010].GenProtRangeError", 44, 44 }, ++ /* 8 */ { "errors/errors", "PORT_ERROR[0x1010].L1prMesegError", 43, 43 }, ++ /* 9 */ { "errors/errors", "PORT_ERROR[0x1010].L1prSmrr2Error", 42, 42 }, ++ /* 10 */ { "errors/errors", "PORT_ERROR[0x1010].L1prSmrrError", 41, 41 }, ++ /* 11 */ { "errors/errors", "PORT_ERROR[0x1010].TxReqCounterOverflow", 40, 40 }, ++ /* 12 */ { "errors/errors", "PORT_ERROR[0x1010].UnexpMMIOResp", 34, 34 }, ++ /* 13 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh2FifoOverflow", 33, 33 }, ++ /* 14 */ { "errors/errors", "PORT_ERROR[0x1010].MMIOTimedOut", 32, 32 }, ++ /* 15 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1NonZeroSOP", 24, 24 }, ++ /* 16 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1IncorrectAddr", 23, 23 }, ++ /* 17 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1DataPayloadOverrun", 22, 22 }, ++ /* 18 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1InsufficientData", 21, 21 }, ++ /* 19 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Len4NotAligned", 20, 20 }, ++ /* 20 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Len2NotAligned", 19, 19 }, ++ /* 21 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Len3NotSupported", 18, 18 }, ++ /* 22 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1InvalidReqEncoding", 17, 17 }, ++ /* 23 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Overflow", 16, 16 }, ++ /* 24 */ { "errors/errors", "PORT_ERROR[0x1010].MMIOWrWhileRst", 10, 10 }, ++ /* 25 */ { "errors/errors", "PORT_ERROR[0x1010].MMIORdWhileRst", 9, 9 }, ++ /* 26 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Len4NotAligned", 4, 4 }, ++ /* 27 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Len2NotAligned", 3, 3 }, ++ /* 28 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Len3NotSupported", 2, 2 }, ++ /* 29 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0InvalidReqEncoding", 1, 1 }, ++ /* 30 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Overflow", 0, 0 }, ++ ++ /* 31 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxReqCounterOverflow", 40, 40 }, ++ /* 32 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh2FifoOverflow", 33, 33 }, ++ /* 33 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].MMIOTimedOut", 32, 32 }, ++ /* 34 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1IllegalVCsel", 25, 25 }, ++ /* 35 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1NonZeroSOP", 24, 24 }, ++ /* 36 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1IncorrectAddr", 23, 23 }, ++ /* 37 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1DataPayloadOverrun", 22, 22 }, ++ /* 38 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1InsufficientData", 21, 21 }, ++ /* 39 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Len4NotAligned", 20, 20 }, ++ /* 40 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Len2NotAligned", 19, 19 }, ++ /* 41 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Len3NotSupported", 18, 18 }, ++ /* 42 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1InvalidReqEncoding", 17, 17 }, ++ /* 43 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Overflow", 16, 16 }, ++ /* 44 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].MMIOWrWhileRst", 10, 10 }, ++ /* 45 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].MMIORdWhileRst", 9, 9 }, ++ /* 46 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Len4NotAligned", 4, 4 }, ++ /* 47 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Len2NotAligned", 3, 3 }, ++ /* 48 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Len3NotSupported", 2, 2 }, ++ /* 49 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0InvalidReqEncoding", 1, 1 }, ++ /* 50 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Overflow", 0, 0 }, ++ ++ /* 51 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].CvlCdcParErro0", 17, 19 }, ++ /* 52 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].Pcie1CdcParErr", 12, 16 }, ++ /* 53 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].Pcie0CdcParErr", 7, 11 }, ++ /* 54 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].MBPErr", 6, 6 }, ++ /* 55 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].AfuAccessModeErr", 5, 5 }, ++ /* 56 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].IommuParityErr", 4, 4 }, ++ /* 57 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].KtiCdcParityErr", 2, 3 }, ++ /* 58 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].FabricFifoUOflow", 1, 1 }, ++ /* 59 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].FabricErr", 0, 0 }, ++ ++ /* 60 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].FunctTypeErr", 63, 63 }, ++ /* 61 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].VFNumb", 62, 62 }, ++ /* 62 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].RxPoisonTlpErr", 9, 9 }, ++ /* 63 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].ParityErr", 8, 8 }, ++ /* 64 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].CompTimeOutErr", 7, 7 }, ++ /* 65 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].CompStatErr", 6, 6 }, ++ /* 66 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].CompTagErr", 5, 5 }, ++ /* 67 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MRLengthErr", 4, 4 }, ++ /* 68 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MRAddrErr", 3, 3 }, ++ /* 69 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MWLengthErr", 2, 2 }, ++ /* 70 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MWAddrErr", 1, 1 }, ++ /* 71 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].FormatTypeErr", 0, 0 }, ++ ++ /* 72 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].RxPoisonTlpErr", 9, 9 }, ++ /* 73 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].ParityErr", 8, 8 }, ++ /* 74 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].CompTimeOutErr", 7, 7 }, ++ /* 75 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].CompStatErr", 6, 6 }, ++ /* 76 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].CompTagErr", 5, 5 }, ++ /* 77 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MRLengthErr", 4, 4 }, ++ /* 78 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MRAddrErr", 3, 3 }, ++ /* 79 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MWLengthErr", 2, 2 }, ++ /* 80 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MWAddrErr", 1, 1 }, ++ /* 81 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].FormatTypeErr", 0, 0 }, ++ ++ /* 82 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].MBPErr", 12, 12 }, ++ /* 83 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PowerThreshAP2", 11, 11 }, ++ /* 84 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PowerThreshAP1", 10, 10 }, ++ /* 85 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].TempThreshAP6", 9, 9 }, /* AP6 */ ++ /* 86 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].InjectedWarningErr", 6, 6 }, ++ /* 87 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].AfuAccessModeErr", 5, 5 }, ++ /* 88 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].ProcHot", 4, 4 }, ++ /* 89 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PortFatalErr", 3, 3 }, ++ /* 90 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PcieError", 2, 2 }, ++ /* 91 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].TempThreshAP2", 1, 1 }, ++ /* 92 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].TempThreshAP1", 0, 0 }, ++ ++ /* 93 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].InjectedCatastErr", 11, 11 }, ++ /* 94 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].ThermCatastErr", 10, 10 }, ++ /* 95 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].CrcCatastErr", 9, 9 }, ++ /* 96 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].InjectedFatalErr", 8, 8 }, ++ /* 97 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].PciePoisonErr", 7, 7 }, ++ /* 98 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].FabricFatalErr", 6, 6 }, ++ /* 99 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].IommuFatalErr", 5, 5 }, ++ /*100 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].DramFatalErr", 4, 4 }, ++ /*101 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].KtiProtoFatalErr", 3, 3 }, ++ /*102 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].CciFatalErr", 2, 2 }, ++ /*103 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].TagCchFatalErr", 1, 1 }, ++ /*104 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].KtiLinkFatalErr", 0, 0 }, ++}; ++ ++fpgad_detection_status ++fpgad_xfpga_detect_Error(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_Error_context *c = ++ (fpgad_xfpga_Error_context *)context; ++ fpga_object obj = NULL; ++ fpga_result res; ++ uint64_t err = 0; ++ uint64_t mask; ++ uint64_t value; ++ int i; ++ bool detected = false; ++ ++ res = fpgaTokenGetObject(d->token, c->sysfs_file, ++ &obj, 0); ++ if (res != FPGA_OK) { ++ LOG("failed to get error object\n"); ++ return FPGAD_STATUS_NOT_DETECTED; ++ } ++ ++ res = fpgaObjectRead64(obj, &err, 0); ++ if (res != FPGA_OK) { ++ LOG("failed to read error object\n"); ++ fpgaDestroyObject(&obj); ++ return FPGAD_STATUS_NOT_DETECTED; ++ } ++ ++ fpgaDestroyObject(&obj); ++ ++ mask = 0; ++ for (i = c->low_bit ; i <= c->high_bit ; ++i) ++ mask |= 1ULL << i; ++ ++ value = mask & err; ++ ++ if (value != 0 && !mon_has_error_occurred(d, context)) { ++ detected = mon_add_device_error(d, context); ++ } ++ ++ if (value == 0 && mon_has_error_occurred(d, context)) { ++ mon_remove_device_error(d, context); ++ } ++ ++ return detected ? FPGAD_STATUS_DETECTED : FPGAD_STATUS_NOT_DETECTED; ++} ++ ++fpgad_detection_status ++fpgad_xfpga_detect_High_Priority_Error(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_detection_status status; ++ ++ status = fpgad_xfpga_detect_Error(d, context); ++ ++ if (status == FPGAD_STATUS_DETECTED) ++ return FPGAD_STATUS_DETECTED_HIGH; ++ ++ return status; ++} ++ ++void fpgad_xfpga_respond_LogError(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_Error_context *c = ++ (fpgad_xfpga_Error_context *)context; ++ ++ LOG("%s\n", c->message); ++ ++ // signal OPAE events API ++ opae_api_send_EVENT_ERROR(d); ++} ++ ++void fpgad_xfpga_respond_AP6_and_Null_GBS(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_Error_context *c = ++ (fpgad_xfpga_Error_context *)context; ++ ++ LOG("%s\n", c->message); ++ ++ // Program NULL GBS ++ ++ // d will be the Port device. We need to find the parent ++ // (FME) device to perform the PR. ++ ++ if (d->bitstr) { ++ fpga_result res; ++ fpga_properties prop = NULL; ++ fpga_token fme_tok = NULL; ++ fpga_handle fme_h = NULL; ++ const uint32_t slot = 0; ++ ++ res = fpgaGetProperties(d->token, &prop); ++ if (res != FPGA_OK) { ++ LOG("(AP6) failed to get properties! : %s\n", ++ fpgaErrStr(res)); ++ goto out_signal; ++ } ++ ++ res = fpgaPropertiesGetParent(prop, &fme_tok); ++ if (res != FPGA_OK || !fme_tok) { ++ LOG("(AP6) failed to get FME token! : %s\n", ++ fpgaErrStr(res)); ++ goto out_destroy_props; ++ } ++ ++ res = fpgaOpen(fme_tok, &fme_h, 0); ++ if (res != FPGA_OK) { ++ LOG("(AP6) failed to get FME handle! : %s\n", ++ fpgaErrStr(res)); ++ goto out_destroy_fme_tok; ++ } ++ ++ LOG("programming \"%s\": ", d->bitstr->filename); ++ ++ res = fpgaReconfigureSlot(fme_h, ++ slot, ++ d->bitstr->data, ++ d->bitstr->data_len, ++ FPGA_RECONF_FORCE); ++ if (res != FPGA_OK) ++ LOG("SUCCESS\n"); ++ else ++ LOG("FAILED : %s\n", fpgaErrStr(res)); ++ ++ fpgaClose(fme_h); ++out_destroy_fme_tok: ++ fpgaDestroyToken(&fme_tok); ++out_destroy_props: ++ fpgaDestroyProperties(&prop); ++ } else ++ LOG("no bitstream to program for AP6!\n"); ++ ++ // Signal OPAE events API ++out_signal: ++ opae_api_send_EVENT_POWER_THERMAL(d); ++} ++ ++void fpgad_xfpga_respond_AP6(fpgad_monitored_device *d, ++ void *context) ++{ ++ fpgad_xfpga_Error_context *c = ++ (fpgad_xfpga_Error_context *)context; ++ ++ LOG("%s\n", c->message); ++ ++ // Signal OPAE events API ++ opae_api_send_EVENT_POWER_THERMAL(d); ++} ++ ++// Port detections ++STATIC fpgad_detect_event_t fpgad_xfpga_port_detections[] = { ++ fpgad_xfpga_detect_AP1_or_AP2, ++ fpgad_xfpga_detect_AP1_or_AP2, ++ fpgad_xfpga_detect_PowerStateChange, ++ ++ fpgad_xfpga_detect_Error, // 0 ++ fpgad_xfpga_detect_High_Priority_Error, // 1 (AP6 and NULL GBS) ++ fpgad_xfpga_detect_Error, // 2 ++ fpgad_xfpga_detect_Error, // 3 ++ fpgad_xfpga_detect_Error, // 4 ++ fpgad_xfpga_detect_Error, // 5 ++ fpgad_xfpga_detect_Error, // 6 ++ fpgad_xfpga_detect_Error, // 7 ++ fpgad_xfpga_detect_Error, // 8 ++ fpgad_xfpga_detect_Error, // 9 ++ fpgad_xfpga_detect_Error, // 10 ++ fpgad_xfpga_detect_Error, // 11 ++ fpgad_xfpga_detect_Error, // 12 ++ fpgad_xfpga_detect_Error, // 13 ++ fpgad_xfpga_detect_Error, // 14 ++ fpgad_xfpga_detect_Error, // 15 ++ fpgad_xfpga_detect_Error, // 16 ++ fpgad_xfpga_detect_Error, // 17 ++ fpgad_xfpga_detect_Error, // 18 ++ fpgad_xfpga_detect_Error, // 19 ++ fpgad_xfpga_detect_Error, // 20 ++ fpgad_xfpga_detect_Error, // 21 ++ fpgad_xfpga_detect_Error, // 22 ++ fpgad_xfpga_detect_Error, // 23 ++ fpgad_xfpga_detect_Error, // 24 ++ fpgad_xfpga_detect_Error, // 25 ++ fpgad_xfpga_detect_Error, // 26 ++ fpgad_xfpga_detect_Error, // 27 ++ fpgad_xfpga_detect_Error, // 28 ++ fpgad_xfpga_detect_Error, // 29 ++ fpgad_xfpga_detect_Error, // 30 ++ ++ fpgad_xfpga_detect_Error, // 31 ++ fpgad_xfpga_detect_Error, // 32 ++ fpgad_xfpga_detect_Error, // 33 ++ fpgad_xfpga_detect_Error, // 34 ++ fpgad_xfpga_detect_Error, // 35 ++ fpgad_xfpga_detect_Error, // 36 ++ fpgad_xfpga_detect_Error, // 37 ++ fpgad_xfpga_detect_Error, // 38 ++ fpgad_xfpga_detect_Error, // 39 ++ fpgad_xfpga_detect_Error, // 40 ++ fpgad_xfpga_detect_Error, // 41 ++ fpgad_xfpga_detect_Error, // 42 ++ fpgad_xfpga_detect_Error, // 43 ++ fpgad_xfpga_detect_Error, // 44 ++ fpgad_xfpga_detect_Error, // 45 ++ fpgad_xfpga_detect_Error, // 46 ++ fpgad_xfpga_detect_Error, // 47 ++ fpgad_xfpga_detect_Error, // 48 ++ fpgad_xfpga_detect_Error, // 49 ++ fpgad_xfpga_detect_Error, // 50 ++ ++ NULL ++}; ++ ++STATIC void *fpgad_xfpga_port_detection_contexts[] = { ++ &fpgad_xfpga_AP_contexts[0], ++ &fpgad_xfpga_AP_contexts[1], ++ &fpgad_xfpga_AP_contexts[2], ++ ++ &fpgad_xfpga_Error_contexts[0], ++ &fpgad_xfpga_Error_contexts[1], ++ &fpgad_xfpga_Error_contexts[2], ++ &fpgad_xfpga_Error_contexts[3], ++ &fpgad_xfpga_Error_contexts[4], ++ &fpgad_xfpga_Error_contexts[5], ++ &fpgad_xfpga_Error_contexts[6], ++ &fpgad_xfpga_Error_contexts[7], ++ &fpgad_xfpga_Error_contexts[8], ++ &fpgad_xfpga_Error_contexts[9], ++ &fpgad_xfpga_Error_contexts[10], ++ &fpgad_xfpga_Error_contexts[11], ++ &fpgad_xfpga_Error_contexts[12], ++ &fpgad_xfpga_Error_contexts[13], ++ &fpgad_xfpga_Error_contexts[14], ++ &fpgad_xfpga_Error_contexts[15], ++ &fpgad_xfpga_Error_contexts[16], ++ &fpgad_xfpga_Error_contexts[17], ++ &fpgad_xfpga_Error_contexts[18], ++ &fpgad_xfpga_Error_contexts[19], ++ &fpgad_xfpga_Error_contexts[20], ++ &fpgad_xfpga_Error_contexts[21], ++ &fpgad_xfpga_Error_contexts[22], ++ &fpgad_xfpga_Error_contexts[23], ++ &fpgad_xfpga_Error_contexts[24], ++ &fpgad_xfpga_Error_contexts[25], ++ &fpgad_xfpga_Error_contexts[26], ++ &fpgad_xfpga_Error_contexts[27], ++ &fpgad_xfpga_Error_contexts[28], ++ &fpgad_xfpga_Error_contexts[29], ++ &fpgad_xfpga_Error_contexts[30], ++ ++ &fpgad_xfpga_Error_contexts[31], ++ &fpgad_xfpga_Error_contexts[32], ++ &fpgad_xfpga_Error_contexts[33], ++ &fpgad_xfpga_Error_contexts[34], ++ &fpgad_xfpga_Error_contexts[35], ++ &fpgad_xfpga_Error_contexts[36], ++ &fpgad_xfpga_Error_contexts[37], ++ &fpgad_xfpga_Error_contexts[38], ++ &fpgad_xfpga_Error_contexts[39], ++ &fpgad_xfpga_Error_contexts[40], ++ &fpgad_xfpga_Error_contexts[41], ++ &fpgad_xfpga_Error_contexts[42], ++ &fpgad_xfpga_Error_contexts[43], ++ &fpgad_xfpga_Error_contexts[44], ++ &fpgad_xfpga_Error_contexts[45], ++ &fpgad_xfpga_Error_contexts[46], ++ &fpgad_xfpga_Error_contexts[47], ++ &fpgad_xfpga_Error_contexts[48], ++ &fpgad_xfpga_Error_contexts[49], ++ &fpgad_xfpga_Error_contexts[50], ++ ++ NULL ++}; ++ ++// Port responses ++STATIC fpgad_respond_event_t fpgad_xfpga_port_responses[] = { ++ fpgad_xfpga_respond_AP1_or_AP2, ++ fpgad_xfpga_respond_AP1_or_AP2, ++ fpgad_xfpga_respond_PowerStateChange, ++ ++ fpgad_xfpga_respond_LogError, // 0 ++ fpgad_xfpga_respond_AP6_and_Null_GBS, // 1 ++ fpgad_xfpga_respond_LogError, // 2 ++ fpgad_xfpga_respond_LogError, // 3 ++ fpgad_xfpga_respond_LogError, // 4 ++ fpgad_xfpga_respond_LogError, // 5 ++ fpgad_xfpga_respond_LogError, // 6 ++ fpgad_xfpga_respond_LogError, // 7 ++ fpgad_xfpga_respond_LogError, // 8 ++ fpgad_xfpga_respond_LogError, // 9 ++ fpgad_xfpga_respond_LogError, // 10 ++ fpgad_xfpga_respond_LogError, // 11 ++ fpgad_xfpga_respond_LogError, // 12 ++ fpgad_xfpga_respond_LogError, // 13 ++ fpgad_xfpga_respond_LogError, // 14 ++ fpgad_xfpga_respond_LogError, // 15 ++ fpgad_xfpga_respond_LogError, // 16 ++ fpgad_xfpga_respond_LogError, // 17 ++ fpgad_xfpga_respond_LogError, // 18 ++ fpgad_xfpga_respond_LogError, // 19 ++ fpgad_xfpga_respond_LogError, // 20 ++ fpgad_xfpga_respond_LogError, // 21 ++ fpgad_xfpga_respond_LogError, // 22 ++ fpgad_xfpga_respond_LogError, // 23 ++ fpgad_xfpga_respond_LogError, // 24 ++ fpgad_xfpga_respond_LogError, // 25 ++ fpgad_xfpga_respond_LogError, // 26 ++ fpgad_xfpga_respond_LogError, // 27 ++ fpgad_xfpga_respond_LogError, // 28 ++ fpgad_xfpga_respond_LogError, // 29 ++ fpgad_xfpga_respond_LogError, // 30 ++ ++ fpgad_xfpga_respond_LogError, // 31 ++ fpgad_xfpga_respond_LogError, // 32 ++ fpgad_xfpga_respond_LogError, // 33 ++ fpgad_xfpga_respond_LogError, // 34 ++ fpgad_xfpga_respond_LogError, // 35 ++ fpgad_xfpga_respond_LogError, // 36 ++ fpgad_xfpga_respond_LogError, // 37 ++ fpgad_xfpga_respond_LogError, // 38 ++ fpgad_xfpga_respond_LogError, // 39 ++ fpgad_xfpga_respond_LogError, // 40 ++ fpgad_xfpga_respond_LogError, // 41 ++ fpgad_xfpga_respond_LogError, // 42 ++ fpgad_xfpga_respond_LogError, // 43 ++ fpgad_xfpga_respond_LogError, // 44 ++ fpgad_xfpga_respond_LogError, // 45 ++ fpgad_xfpga_respond_LogError, // 46 ++ fpgad_xfpga_respond_LogError, // 47 ++ fpgad_xfpga_respond_LogError, // 48 ++ fpgad_xfpga_respond_LogError, // 49 ++ fpgad_xfpga_respond_LogError, // 50 ++ ++ NULL ++}; ++ ++STATIC void *fpgad_xfpga_port_response_contexts[] = { ++ &fpgad_xfpga_AP_contexts[0], ++ &fpgad_xfpga_AP_contexts[1], ++ &fpgad_xfpga_AP_contexts[2], ++ ++ &fpgad_xfpga_Error_contexts[0], ++ &fpgad_xfpga_Error_contexts[1], ++ &fpgad_xfpga_Error_contexts[2], ++ &fpgad_xfpga_Error_contexts[3], ++ &fpgad_xfpga_Error_contexts[4], ++ &fpgad_xfpga_Error_contexts[5], ++ &fpgad_xfpga_Error_contexts[6], ++ &fpgad_xfpga_Error_contexts[7], ++ &fpgad_xfpga_Error_contexts[8], ++ &fpgad_xfpga_Error_contexts[9], ++ &fpgad_xfpga_Error_contexts[10], ++ &fpgad_xfpga_Error_contexts[11], ++ &fpgad_xfpga_Error_contexts[12], ++ &fpgad_xfpga_Error_contexts[13], ++ &fpgad_xfpga_Error_contexts[14], ++ &fpgad_xfpga_Error_contexts[15], ++ &fpgad_xfpga_Error_contexts[16], ++ &fpgad_xfpga_Error_contexts[17], ++ &fpgad_xfpga_Error_contexts[18], ++ &fpgad_xfpga_Error_contexts[19], ++ &fpgad_xfpga_Error_contexts[20], ++ &fpgad_xfpga_Error_contexts[21], ++ &fpgad_xfpga_Error_contexts[22], ++ &fpgad_xfpga_Error_contexts[23], ++ &fpgad_xfpga_Error_contexts[24], ++ &fpgad_xfpga_Error_contexts[25], ++ &fpgad_xfpga_Error_contexts[26], ++ &fpgad_xfpga_Error_contexts[27], ++ &fpgad_xfpga_Error_contexts[28], ++ &fpgad_xfpga_Error_contexts[29], ++ &fpgad_xfpga_Error_contexts[30], ++ ++ &fpgad_xfpga_Error_contexts[31], ++ &fpgad_xfpga_Error_contexts[32], ++ &fpgad_xfpga_Error_contexts[33], ++ &fpgad_xfpga_Error_contexts[34], ++ &fpgad_xfpga_Error_contexts[35], ++ &fpgad_xfpga_Error_contexts[36], ++ &fpgad_xfpga_Error_contexts[37], ++ &fpgad_xfpga_Error_contexts[38], ++ &fpgad_xfpga_Error_contexts[39], ++ &fpgad_xfpga_Error_contexts[40], ++ &fpgad_xfpga_Error_contexts[41], ++ &fpgad_xfpga_Error_contexts[42], ++ &fpgad_xfpga_Error_contexts[43], ++ &fpgad_xfpga_Error_contexts[44], ++ &fpgad_xfpga_Error_contexts[45], ++ &fpgad_xfpga_Error_contexts[46], ++ &fpgad_xfpga_Error_contexts[47], ++ &fpgad_xfpga_Error_contexts[48], ++ &fpgad_xfpga_Error_contexts[49], ++ &fpgad_xfpga_Error_contexts[50], ++ ++ NULL ++}; ++ ++// FME detections ++STATIC fpgad_detect_event_t fpgad_xfpga_fme_detections[] = { ++ fpgad_xfpga_detect_Error, // 51 ++ fpgad_xfpga_detect_Error, // 52 ++ fpgad_xfpga_detect_Error, // 53 ++ fpgad_xfpga_detect_Error, // 54 ++ fpgad_xfpga_detect_Error, // 55 ++ fpgad_xfpga_detect_Error, // 56 ++ fpgad_xfpga_detect_Error, // 57 ++ fpgad_xfpga_detect_Error, // 58 ++ fpgad_xfpga_detect_Error, // 59 ++ ++ fpgad_xfpga_detect_Error, // 60 ++ fpgad_xfpga_detect_Error, // 61 ++ fpgad_xfpga_detect_Error, // 62 ++ fpgad_xfpga_detect_Error, // 63 ++ fpgad_xfpga_detect_Error, // 64 ++ fpgad_xfpga_detect_Error, // 65 ++ fpgad_xfpga_detect_Error, // 66 ++ fpgad_xfpga_detect_Error, // 67 ++ fpgad_xfpga_detect_Error, // 68 ++ fpgad_xfpga_detect_Error, // 69 ++ fpgad_xfpga_detect_Error, // 70 ++ fpgad_xfpga_detect_Error, // 71 ++ ++ fpgad_xfpga_detect_Error, // 72 ++ fpgad_xfpga_detect_Error, // 73 ++ fpgad_xfpga_detect_Error, // 74 ++ fpgad_xfpga_detect_Error, // 75 ++ fpgad_xfpga_detect_Error, // 76 ++ fpgad_xfpga_detect_Error, // 77 ++ fpgad_xfpga_detect_Error, // 78 ++ fpgad_xfpga_detect_Error, // 79 ++ fpgad_xfpga_detect_Error, // 80 ++ fpgad_xfpga_detect_Error, // 81 ++ ++ fpgad_xfpga_detect_Error, // 82 ++ fpgad_xfpga_detect_Error, // 83 ++ fpgad_xfpga_detect_Error, // 84 ++ fpgad_xfpga_detect_Error, // 85 ++ fpgad_xfpga_detect_Error, // 86 ++ fpgad_xfpga_detect_Error, // 87 ++ fpgad_xfpga_detect_Error, // 88 ++ fpgad_xfpga_detect_Error, // 89 ++ fpgad_xfpga_detect_Error, // 90 ++ fpgad_xfpga_detect_Error, // 91 ++ fpgad_xfpga_detect_Error, // 92 ++ ++ fpgad_xfpga_detect_Error, // 93 ++ fpgad_xfpga_detect_Error, // 94 ++ fpgad_xfpga_detect_Error, // 95 ++ fpgad_xfpga_detect_Error, // 96 ++ fpgad_xfpga_detect_Error, // 97 ++ fpgad_xfpga_detect_Error, // 98 ++ fpgad_xfpga_detect_Error, // 99 ++ fpgad_xfpga_detect_Error, //100 ++ fpgad_xfpga_detect_Error, //101 ++ fpgad_xfpga_detect_Error, //102 ++ fpgad_xfpga_detect_Error, //103 ++ fpgad_xfpga_detect_Error, //104 ++ ++ NULL ++}; ++ ++STATIC void *fpgad_xfpga_fme_detection_contexts[] = { ++ &fpgad_xfpga_Error_contexts[51], ++ &fpgad_xfpga_Error_contexts[52], ++ &fpgad_xfpga_Error_contexts[53], ++ &fpgad_xfpga_Error_contexts[54], ++ &fpgad_xfpga_Error_contexts[55], ++ &fpgad_xfpga_Error_contexts[56], ++ &fpgad_xfpga_Error_contexts[57], ++ &fpgad_xfpga_Error_contexts[58], ++ &fpgad_xfpga_Error_contexts[59], ++ ++ &fpgad_xfpga_Error_contexts[60], ++ &fpgad_xfpga_Error_contexts[61], ++ &fpgad_xfpga_Error_contexts[62], ++ &fpgad_xfpga_Error_contexts[63], ++ &fpgad_xfpga_Error_contexts[64], ++ &fpgad_xfpga_Error_contexts[65], ++ &fpgad_xfpga_Error_contexts[66], ++ &fpgad_xfpga_Error_contexts[67], ++ &fpgad_xfpga_Error_contexts[68], ++ &fpgad_xfpga_Error_contexts[69], ++ &fpgad_xfpga_Error_contexts[70], ++ &fpgad_xfpga_Error_contexts[71], ++ ++ &fpgad_xfpga_Error_contexts[72], ++ &fpgad_xfpga_Error_contexts[73], ++ &fpgad_xfpga_Error_contexts[74], ++ &fpgad_xfpga_Error_contexts[75], ++ &fpgad_xfpga_Error_contexts[76], ++ &fpgad_xfpga_Error_contexts[77], ++ &fpgad_xfpga_Error_contexts[78], ++ &fpgad_xfpga_Error_contexts[79], ++ &fpgad_xfpga_Error_contexts[80], ++ &fpgad_xfpga_Error_contexts[81], ++ ++ &fpgad_xfpga_Error_contexts[82], ++ &fpgad_xfpga_Error_contexts[83], ++ &fpgad_xfpga_Error_contexts[84], ++ &fpgad_xfpga_Error_contexts[85], ++ &fpgad_xfpga_Error_contexts[86], ++ &fpgad_xfpga_Error_contexts[87], ++ &fpgad_xfpga_Error_contexts[88], ++ &fpgad_xfpga_Error_contexts[89], ++ &fpgad_xfpga_Error_contexts[90], ++ &fpgad_xfpga_Error_contexts[91], ++ &fpgad_xfpga_Error_contexts[92], ++ ++ &fpgad_xfpga_Error_contexts[93], ++ &fpgad_xfpga_Error_contexts[94], ++ &fpgad_xfpga_Error_contexts[95], ++ &fpgad_xfpga_Error_contexts[96], ++ &fpgad_xfpga_Error_contexts[97], ++ &fpgad_xfpga_Error_contexts[98], ++ &fpgad_xfpga_Error_contexts[99], ++ &fpgad_xfpga_Error_contexts[100], ++ &fpgad_xfpga_Error_contexts[101], ++ &fpgad_xfpga_Error_contexts[102], ++ &fpgad_xfpga_Error_contexts[103], ++ &fpgad_xfpga_Error_contexts[104], ++ ++ NULL ++}; ++ ++// FME responses ++STATIC fpgad_respond_event_t fpgad_xfpga_fme_responses[] = { ++ fpgad_xfpga_respond_LogError, // 51 ++ fpgad_xfpga_respond_LogError, // 52 ++ fpgad_xfpga_respond_LogError, // 53 ++ fpgad_xfpga_respond_LogError, // 54 ++ fpgad_xfpga_respond_LogError, // 55 ++ fpgad_xfpga_respond_LogError, // 56 ++ fpgad_xfpga_respond_LogError, // 57 ++ fpgad_xfpga_respond_LogError, // 58 ++ fpgad_xfpga_respond_LogError, // 59 ++ ++ fpgad_xfpga_respond_LogError, // 60 ++ fpgad_xfpga_respond_LogError, // 61 ++ fpgad_xfpga_respond_LogError, // 62 ++ fpgad_xfpga_respond_LogError, // 63 ++ fpgad_xfpga_respond_LogError, // 64 ++ fpgad_xfpga_respond_LogError, // 65 ++ fpgad_xfpga_respond_LogError, // 66 ++ fpgad_xfpga_respond_LogError, // 67 ++ fpgad_xfpga_respond_LogError, // 68 ++ fpgad_xfpga_respond_LogError, // 69 ++ fpgad_xfpga_respond_LogError, // 70 ++ fpgad_xfpga_respond_LogError, // 71 ++ ++ fpgad_xfpga_respond_LogError, // 72 ++ fpgad_xfpga_respond_LogError, // 73 ++ fpgad_xfpga_respond_LogError, // 74 ++ fpgad_xfpga_respond_LogError, // 75 ++ fpgad_xfpga_respond_LogError, // 76 ++ fpgad_xfpga_respond_LogError, // 77 ++ fpgad_xfpga_respond_LogError, // 78 ++ fpgad_xfpga_respond_LogError, // 79 ++ fpgad_xfpga_respond_LogError, // 80 ++ fpgad_xfpga_respond_LogError, // 81 ++ ++ fpgad_xfpga_respond_LogError, // 82 ++ fpgad_xfpga_respond_LogError, // 83 ++ fpgad_xfpga_respond_LogError, // 84 ++ fpgad_xfpga_respond_AP6, // 85 ++ fpgad_xfpga_respond_LogError, // 86 ++ fpgad_xfpga_respond_LogError, // 87 ++ fpgad_xfpga_respond_LogError, // 88 ++ fpgad_xfpga_respond_LogError, // 89 ++ fpgad_xfpga_respond_LogError, // 90 ++ fpgad_xfpga_respond_LogError, // 91 ++ fpgad_xfpga_respond_LogError, // 92 ++ ++ fpgad_xfpga_respond_LogError, // 93 ++ fpgad_xfpga_respond_LogError, // 94 ++ fpgad_xfpga_respond_LogError, // 95 ++ fpgad_xfpga_respond_LogError, // 96 ++ fpgad_xfpga_respond_LogError, // 97 ++ fpgad_xfpga_respond_LogError, // 98 ++ fpgad_xfpga_respond_LogError, // 99 ++ fpgad_xfpga_respond_LogError, //100 ++ fpgad_xfpga_respond_LogError, //101 ++ fpgad_xfpga_respond_LogError, //102 ++ fpgad_xfpga_respond_LogError, //103 ++ fpgad_xfpga_respond_LogError, //104 ++ ++ NULL ++}; ++ ++STATIC void *fpgad_xfpga_fme_response_contexts[] = { ++ &fpgad_xfpga_Error_contexts[51], ++ &fpgad_xfpga_Error_contexts[52], ++ &fpgad_xfpga_Error_contexts[53], ++ &fpgad_xfpga_Error_contexts[54], ++ &fpgad_xfpga_Error_contexts[55], ++ &fpgad_xfpga_Error_contexts[56], ++ &fpgad_xfpga_Error_contexts[57], ++ &fpgad_xfpga_Error_contexts[58], ++ &fpgad_xfpga_Error_contexts[59], ++ ++ &fpgad_xfpga_Error_contexts[60], ++ &fpgad_xfpga_Error_contexts[61], ++ &fpgad_xfpga_Error_contexts[62], ++ &fpgad_xfpga_Error_contexts[63], ++ &fpgad_xfpga_Error_contexts[64], ++ &fpgad_xfpga_Error_contexts[65], ++ &fpgad_xfpga_Error_contexts[66], ++ &fpgad_xfpga_Error_contexts[67], ++ &fpgad_xfpga_Error_contexts[68], ++ &fpgad_xfpga_Error_contexts[69], ++ &fpgad_xfpga_Error_contexts[70], ++ &fpgad_xfpga_Error_contexts[71], ++ ++ &fpgad_xfpga_Error_contexts[72], ++ &fpgad_xfpga_Error_contexts[73], ++ &fpgad_xfpga_Error_contexts[74], ++ &fpgad_xfpga_Error_contexts[75], ++ &fpgad_xfpga_Error_contexts[76], ++ &fpgad_xfpga_Error_contexts[77], ++ &fpgad_xfpga_Error_contexts[78], ++ &fpgad_xfpga_Error_contexts[79], ++ &fpgad_xfpga_Error_contexts[80], ++ &fpgad_xfpga_Error_contexts[81], ++ ++ &fpgad_xfpga_Error_contexts[82], ++ &fpgad_xfpga_Error_contexts[83], ++ &fpgad_xfpga_Error_contexts[84], ++ &fpgad_xfpga_Error_contexts[85], ++ &fpgad_xfpga_Error_contexts[86], ++ &fpgad_xfpga_Error_contexts[87], ++ &fpgad_xfpga_Error_contexts[88], ++ &fpgad_xfpga_Error_contexts[89], ++ &fpgad_xfpga_Error_contexts[90], ++ &fpgad_xfpga_Error_contexts[91], ++ &fpgad_xfpga_Error_contexts[92], ++ ++ &fpgad_xfpga_Error_contexts[93], ++ &fpgad_xfpga_Error_contexts[94], ++ &fpgad_xfpga_Error_contexts[95], ++ &fpgad_xfpga_Error_contexts[96], ++ &fpgad_xfpga_Error_contexts[97], ++ &fpgad_xfpga_Error_contexts[98], ++ &fpgad_xfpga_Error_contexts[99], ++ &fpgad_xfpga_Error_contexts[100], ++ &fpgad_xfpga_Error_contexts[101], ++ &fpgad_xfpga_Error_contexts[102], ++ &fpgad_xfpga_Error_contexts[103], ++ &fpgad_xfpga_Error_contexts[104], ++ ++ NULL ++}; ++ ++int fpgad_plugin_configure(fpgad_monitored_device *d, ++ const char *cfg) ++{ ++ UNUSED_PARAM(cfg); ++ ++ LOG("monitoring vid=0x%04x did=0x%04x objid=0x%x (%s)\n", ++ d->supported->vendor_id, ++ d->supported->device_id, ++ d->object_id, ++ d->object_type == FPGA_ACCELERATOR ? ++ "accelerator" : "device"); ++ ++ d->type = FPGAD_PLUGIN_TYPE_CALLBACK; ++ ++ if (d->object_type == FPGA_ACCELERATOR) { ++ d->detections = fpgad_xfpga_port_detections; ++ d->detection_contexts = fpgad_xfpga_port_detection_contexts; ++ d->responses = fpgad_xfpga_port_responses; ++ d->response_contexts = fpgad_xfpga_port_response_contexts; ++ } else { ++ d->detections = fpgad_xfpga_fme_detections; ++ d->detection_contexts = fpgad_xfpga_fme_detection_contexts; ++ d->responses = fpgad_xfpga_fme_responses; ++ d->response_contexts = fpgad_xfpga_fme_response_contexts; ++ } ++ ++ return 0; ++} ++ ++void fpgad_plugin_destroy(fpgad_monitored_device *d) ++{ ++ LOG("stop monitoring vid=0x%04x did=0x%04x objid=0x%x (%s)\n", ++ d->supported->vendor_id, ++ d->supported->device_id, ++ d->object_id, ++ d->object_type == FPGA_ACCELERATOR ? ++ "accelerator" : "device"); ++} +-- +2.18.2 + diff --git a/SOURCES/0001-Reinclude-fpgaport-in-tools.patch b/SOURCES/0001-Reinclude-fpgaport-in-tools.patch new file mode 100644 index 0000000..19338f4 --- /dev/null +++ b/SOURCES/0001-Reinclude-fpgaport-in-tools.patch @@ -0,0 +1,148 @@ +From 76d42fe3e3f2007e4fe18e27e0a68840713cda2a Mon Sep 17 00:00:00 2001 +From: Tom Rix +Date: Wed, 13 May 2020 10:57:37 -0400 +Subject: [PATCH 1/2] Reinclude fpgaport in tools + +In existing documentation fpgaport is the used to do the driver +ioctl call for fme port release and assign. This is needed for +the sr-iov workflow. + +Import from 1.4.0 and change to python3 + +Signed-off-by: Tom Rix +--- + tools/CMakeLists.txt | 1 + + tools/fpgaport/CMakeLists.txt | 29 ++++++++++++++ + tools/fpgaport/fpgaport | 72 +++++++++++++++++++++++++++++++++++ + 3 files changed, 102 insertions(+) + create mode 100644 tools/fpgaport/CMakeLists.txt + create mode 100755 tools/fpgaport/fpgaport + +diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt +index 9674af02..d4b55036 100644 +--- a/tools/CMakeLists.txt ++++ b/tools/CMakeLists.txt +@@ -31,6 +31,7 @@ opae_add_subdirectory(libboard/board_dc) + opae_add_subdirectory(fpgaconf) + opae_add_subdirectory(fpgainfo) + opae_add_subdirectory(fpgametrics) ++opae_add_subdirectory(fpgaport) + + # extra + opae_add_subdirectory(extra/userclk) +diff --git a/tools/fpgaport/CMakeLists.txt b/tools/fpgaport/CMakeLists.txt +new file mode 100644 +index 00000000..cb16c8e9 +--- /dev/null ++++ b/tools/fpgaport/CMakeLists.txt +@@ -0,0 +1,29 @@ ++## Copyright(c) 2017, Intel Corporation ++## ++## Redistribution and use in source and binary forms, with or without ++## modification, are permitted provided that the following conditions are met: ++## ++## * Redistributions of source code must retain the above copyright notice, ++## this list of conditions and the following disclaimer. ++## * Redistributions in binary form must reproduce the above copyright notice, ++## this list of conditions and the following disclaimer in the documentation ++## and/or other materials provided with the distribution. ++## * Neither the name of Intel Corporation nor the names of its contributors ++## may be used to endorse or promote products derived from this software ++## without specific prior written permission. ++## ++## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++## POSSIBILITY OF SUCH DAMAGE. ++ ++install(PROGRAMS fpgaport ++ DESTINATION bin ++ COMPONENT toolfpgaport) +diff --git a/tools/fpgaport/fpgaport b/tools/fpgaport/fpgaport +new file mode 100755 +index 00000000..2f707183 +--- /dev/null ++++ b/tools/fpgaport/fpgaport +@@ -0,0 +1,72 @@ ++#! /usr/bin/python3 ++# Copyright(c) 2017, Intel Corporation ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are met: ++# ++# * Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# * Neither the name of Intel Corporation nor the names of its contributors ++# may be used to endorse or promote products derived from this software ++# without specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ++# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++# POSSIBILITY OF SUCH DAMAGE. ++ ++import fcntl, os, sys, argparse, stat, struct ++ ++FPGA_FME_PORT_ASSIGN = 0xB582 ++FPGA_FME_PORT_RELEASE = 0xB581 ++ ++if __name__ == "__main__": ++ ++ # parse command line arguments ++ parser = argparse.ArgumentParser() ++ parser.add_argument('action', help = 'action to perform', action = 'store', choices = ['assign', 'release']) ++ parser.add_argument('device', help = 'the FPGA (FME) device') ++ parser.add_argument('port', type = int, help = 'the number of the port') ++ ++ args = parser.parse_args() ++ ++ # check device file ++ try: ++ mode = os.stat(args.device).st_mode ++ if not stat.S_ISCHR(mode): ++ print( args.device, "is not a device file.") ++ except Exception as e: ++ print("Couldn't open", args.device, ":", e) ++ sys.exit(1) ++ ++ # open FPGA device ++ try: ++ f = open(args.device, "rw") ++ except Exception as e: ++ print("open() failed:", e) ++ sys.exit(1) ++ ++ # send IOCTL ++ ioctl_data = struct.pack('III', 12, 0, args.port) ++ try: ++ if args.action == 'assign': ++ ret = fcntl.ioctl(f, FPGA_FME_PORT_ASSIGN, ioctl_data) ++ else: ++ ret = fcntl.ioctl(f, FPGA_FME_PORT_RELEASE, ioctl_data) ++ except Exception as e: ++ print("ioctl() failed:", e) ++ f.close() ++ sys.exit(1) ++ ++ # close FPGA device ++ f.close() +-- +2.18.2 + diff --git a/SOURCES/0002-Add-support-for-public-dfl-driver-to-fpgaport.patch b/SOURCES/0002-Add-support-for-public-dfl-driver-to-fpgaport.patch new file mode 100644 index 0000000..440a8fc --- /dev/null +++ b/SOURCES/0002-Add-support-for-public-dfl-driver-to-fpgaport.patch @@ -0,0 +1,64 @@ +From cd80370722f050f15cbeae121584853fbe44949b Mon Sep 17 00:00:00 2001 +From: Tom Rix +Date: Wed, 13 May 2020 15:06:40 -0400 +Subject: [PATCH 2/2] Add support for public dfl driver to fpgaport + +The ioctl's are different and the data passed is an int. + +Signed-off-by: Tom Rix +--- + tools/fpgaport/fpgaport | 21 +++++++++++++++++---- + 1 file changed, 17 insertions(+), 4 deletions(-) + +diff --git a/tools/fpgaport/fpgaport b/tools/fpgaport/fpgaport +index 2f707183..cf133c98 100755 +--- a/tools/fpgaport/fpgaport ++++ b/tools/fpgaport/fpgaport +@@ -27,8 +27,12 @@ + + import fcntl, os, sys, argparse, stat, struct + ++# Intel driver + FPGA_FME_PORT_ASSIGN = 0xB582 + FPGA_FME_PORT_RELEASE = 0xB581 ++# DFL driver ++DFL_FME_PORT_ASSIGN = 0x4004B682 ++DFL_FME_PORT_RELEASE = 0x4004B681 + + if __name__ == "__main__": + +@@ -51,18 +55,27 @@ if __name__ == "__main__": + + # open FPGA device + try: +- f = open(args.device, "rw") ++ f = open(args.device, "w") + except Exception as e: + print("open() failed:", e) + sys.exit(1) + + # send IOCTL +- ioctl_data = struct.pack('III', 12, 0, args.port) ++ if (args.device.find("intel-fpga-fme") != -1): ++ ioctl_data = struct.pack('III', 12, 0, args.port) ++ else: ++ ioctl_data = struct.pack('I', args.port) + try: + if args.action == 'assign': +- ret = fcntl.ioctl(f, FPGA_FME_PORT_ASSIGN, ioctl_data) ++ if (args.device.find("intel-fpga-fme") != -1): ++ ret = fcntl.ioctl(f, FPGA_FME_PORT_ASSIGN, ioctl_data) ++ else: ++ ret = fcntl.ioctl(f, DFL_FME_PORT_ASSIGN, ioctl_data) + else: +- ret = fcntl.ioctl(f, FPGA_FME_PORT_RELEASE, ioctl_data) ++ if (args.device.find("intel-fpga-fme") != -1): ++ ret = fcntl.ioctl(f, FPGA_FME_PORT_RELEASE, ioctl_data) ++ else: ++ ret = fcntl.ioctl(f, DFL_FME_PORT_RELEASE, ioctl_data) + except Exception as e: + print("ioctl() failed:", e) + f.close() +-- +2.18.2 + diff --git a/SOURCES/improve-library-link.patch b/SOURCES/improve-library-link.patch new file mode 100644 index 0000000..2be2894 --- /dev/null +++ b/SOURCES/improve-library-link.patch @@ -0,0 +1,16 @@ +diff -rup opae-1.4.1-1.orig/opae-libs/libopaecxx/CMakeLists.txt opae-1.4.1-1/opae-libs/libopaecxx/CMakeLists.txt +--- opae-1.4.1-1.orig/opae-libs/libopaecxx/CMakeLists.txt 2020-04-20 06:45:25.055518486 -0700 ++++ opae-1.4.1-1/opae-libs/libopaecxx/CMakeLists.txt 2020-04-20 06:50:19.498371275 -0700 +@@ -40,6 +40,10 @@ set(OPAECXXCORE_SRC + + opae_add_shared_library(TARGET opae-cxx-core + SOURCE ${OPAECXXCORE_SRC} ++ LIBS ++ opae-c ++ ${CMAKE_THREAD_LIBS_INIT} ++ ${libuuid_LIBRARIES} + VERSION ${OPAE_VERSION} + SOVERSION ${OPAE_VERSION_MAJOR} + COMPONENT opaecxxcorelib +Only in opae-1.4.1-1/opae-libs/libopaecxx: CMakeLists.txt~ +Only in opae-1.4.1-1.orig/opae-libs/libopaecxx: CMakeLists.txt.rej diff --git a/SPECS/opae.spec b/SPECS/opae.spec new file mode 100644 index 0000000..107a5e8 --- /dev/null +++ b/SPECS/opae.spec @@ -0,0 +1,244 @@ +Summary: Open Programmable Acceleration Engine (OPAE) SDK +Name: opae +Version: 1.4.1 +Release: 11%{?dist} +License: BSD and MIT +ExclusiveArch: x86_64 +URL: https://github.com/OPAE/%{name}-sdk +Source0: https://github.com/OPAE/opae-sdk/releases/download/%{version}-1/%{name}-%{version}-1.tar.gz +Patch01: 0001-Do-not-install-static-libraries.patch +Patch02: improve-library-link.patch +Patch03: 0001-Reinclude-fpgaport-in-tools.patch +Patch04: 0002-Add-support-for-public-dfl-driver-to-fpgaport.patch +Patch05: 0001-Import-fpgad-from-opae-legacy.patch +Patch06: 0001-Fix-possible-buffer-overflow.patch + +BuildRequires: gcc, gcc-c++ +BuildRequires: cmake, make +BuildRequires: python3-devel +BuildRequires: json-c-devel +BuildRequires: libuuid-devel +BuildRequires: rpm-build +BuildRequires: hwloc-devel +BuildRequires: doxygen + +%description +Open Programmable Acceleration Engine (OPAE) is a software framework +for managing and accessing programmable accelerators (FPGAs). +Its main parts are: + +* OPAE Software Development Kit (OPAE SDK) (this package) +* OPAE Linux driver for Intel(R) Xeon(R) CPU with + Integrated FPGAs and Intel(R) PAC with Arria(R) 10 GX FPGA +* Basic Building Block (BBB) library for accelerating AFU + +OPAE SDK is a collection of libraries and tools to facilitate the +development of software applications and accelerators using OPAE. +It provides a library implementing the OPAE C API for presenting a +streamlined and easy-to-use interface for software applications to +discover, access, and manage FPGA devices and accelerators using +the OPAE software stack. + +%package devel +Summary: OPAE headers, sample source, and documentation +Requires: libuuid-devel, %{name}%{?_isa} = %{version}-%{release} + +%description devel +OPAE headers, tools, sample source, and documentation + +%prep +%setup -q -n %{name}-%{version}-1 +%patch01 -p1 +%patch02 -p1 +%patch03 -p1 +%patch04 -p1 +%patch05 -p1 +%patch06 -p1 + +# Remove hidden .clang-format +rm opae-libs/tests/xfpga/.clang-format +rm tests/.clang-format +rm tools/argsfilter/.clang-format + +%build +mkdir -p _build +cd _build +%cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DOPAE_PRESERVE_REPOS=ON +%make_build + +%install +mkdir -p %{buildroot}%{_datadir}/opae +cp ./RELEASE_NOTES.md %{buildroot}%{_datadir}/opae/RELEASE_NOTES.md +cp ./LICENSE %{buildroot}%{_datadir}/opae/LICENSE +cp ./COPYING %{buildroot}%{_datadir}/opae/COPYING + +# cmake modules +mkdir -p %{buildroot}%{_usr}/src/opae/cmake/modules +for s in FindSphinx.cmake +do + cp "cmake/${s}" %{buildroot}%{_usr}/src/opae/cmake/ +done +mkdir -p %{buildroot}%{_usr}/src/opae/opae-libs/cmake/modules +for s in FindHwloc.cmake \ + OPAE.cmake \ + FindUUID.cmake \ + Findjson-c.cmake \ + OPAECompiler.cmake \ + OPAEGit.cmake \ + OPAEPackaging.cmake +do + cp "opae-libs/cmake/modules/${s}" %{buildroot}%{_usr}/src/opae/opae-libs/cmake/modules +done + +# Samples +mkdir -p %{buildroot}%{_usr}/src/opae/samples +mkdir -p %{buildroot}%{_usr}/src/opae/samples/hello_fpga/ +cp samples/hello_fpga/hello_fpga.c %{buildroot}%{_usr}/src/opae/samples/hello_fpga/ + +%make_install -C _build + +%files +%dir %{_datadir}/opae +%doc %{_datadir}/opae/RELEASE_NOTES.md +%license %{_datadir}/opae/LICENSE +%license %{_datadir}/opae/COPYING +%{_libdir}/libbitstream.so.%{version} +%{_libdir}/libbitstream.so.1 +%{_libdir}/libfpgad-api.so.%{version} +%{_libdir}/libfpgad-api.so.1 +%{_libdir}/libopae-c.so.%{version} +%{_libdir}/libopae-c.so.1 +%{_libdir}/libopae-c-ase.so.%{version} +%{_libdir}/libopae-c-ase.so.1 +%{_libdir}/libopae-cxx-core.so.%{version} +%{_libdir}/libopae-cxx-core.so.1 + +%post devel +%systemd_post fpgad.service + +%preun devel +%systemd_preun fpgad.service + +%files devel +%dir %{_includedir}/opae +%dir %{_libdir}/opae +%dir %{_usr}/src/opae +%dir %{_usr}/src/opae/cmake/ +%dir %{_usr}/src/opae/opae-libs/cmake/modules/ +%dir %{_usr}/src/opae/samples +%config(noreplace) %{_sysconfdir}/opae/fpgad.cfg* +%config(noreplace) %{_sysconfdir}/sysconfig/fpgad.conf* +%{_bindir}/fpgaconf +%{_bindir}/fpgad +%{_bindir}/fpgainfo +%{_bindir}/fpgaport +%{_bindir}/mmlink +%{_bindir}/userclk +%{_bindir}/hello_fpga +%{_bindir}/hello_cxxcore +%{_bindir}/afu_json_mgr +%{_bindir}/packager +%{_includedir}/opae/* +%{_libdir}/libbitstream.so +%{_libdir}/libfpgad-api.so +%{_libdir}/libopae-c.so +%{_libdir}/libopae-c-ase.so +%{_libdir}/libopae-cxx-core.so +%{_libdir}/opae/libboard_rc.so +%{_libdir}/opae/libboard_vc.so +%{_libdir}/opae/libfpgad-vc.so +%{_libdir}/opae/libfpgad-xfpga.so +%{_libdir}/opae/libmodbmc.so +%{_libdir}/opae/libxfpga.so +%{_usr}/share/opae/* +%{_usr}/src/opae/samples/hello_fpga/hello_fpga.c +%{_usr}/src/opae/cmake/* +%{_usr}/src/opae/opae-libs/cmake/modules/* +%{_unitdir}/fpgad.service + +%changelog +* Tue Jun 2 2020 Tom Rix - 1.4.1-11 +- Changelog date + +* Tue Jun 2 2020 Tom Rix - 1.4.1-10 +- Changelog date + +* Tue Jun 2 2020 Tom Rix - 1.4.1-9 +- Add gating + +* Thu May 19 2020 Tom Rix - 1.4.1-8 +- Fix buffer overflow + +* Thu May 14 2020 Tom Rix - 1.4.1-7 +- Import fpgad from opae-legacy + +* Wed May 13 2020 Tom Rix - 1.4.1-6 +- Import fpgaport from 1.4.0 +- Generalize to work with public and intel drivers. + +* Tue Apr 21 2020 Björn Esser - 1.4.1-5 +- Rebuild (json-c) + +* Tue Apr 21 2020 Tom Rix 1.4.1-4 +- Update the sources file + +* Tue Apr 21 2020 Tom Rix 1.4.1-3 +- Update the sources file + +* Mon Apr 20 2020 Tom Rix 1.4.1-2 +- Disable broken documents +- Do not install static libs +- Improve linking of libopae-cxx-core + +* Fri Apr 17 2020 Korde Nakul 1.4.1-1 +- OPAE git repository layout changes. +- Removed Safe String module dependency. +- Various bug fixes. +- Ported python tools to python3.6. +- Various Static code scan bug fixes. +- Removed pybind11 3rd component from OPAE source repository. + +* Tue Mar 10 2020 Tom Rix 1.4.0-6 +- Add make as a dependency + +* Fri Mar 6 2020 Tom Rix 1.4.0-5 +- Use make_install macro +- Use license tag correctly + +* Tue Mar 3 2020 Tom Rix 1.4.0-4 +- Add libraries to link of libopae-cxx-core libopae-c++-utils +- Remove unneeded build flag _smp_mflags + +* Thu Feb 27 2020 Tom Rix 1.4.0-3 +- Remove ldconfig from post and postun +- Append dist tag to release tag +- Change libsafestr to shared library +- Set license tag to location of license files +- Remove phython3-sphnix build dependency. +- Consolidate samples,tools,tools-extra pkgs into devel +- Improve pkg created dir specification +- Set x86_64 as ExclusiveArch +- Change to runtime to implicit dependency on build *-devel +- Remove preun rm of opae-c.conf +- Use systemd rpm macros +- Add _smp_mflags to build +- Use unitdir for fpgad.service path +- Distribute the license and copying files + +* Mon Feb 24 2020 Tom Rix 1.4.0-2 +- Change to python3 +- Remove release tag from upstream Source0 definition. +- Improve requires tag for subpackages +- Remove explicit root owner +- Remove vendor tag +- Remove group tag +- Remove clean section + +* Tue Dec 17 2019 Korde Nakul 1.4.0-1 +- Added support to FPGA Linux kernel Device Feature List (DFL) driver patch set2. +- Increased test cases and test coverage +- Various bug fixes +- Various compiler warning fixes +- Various memory leak fixes +- Various Static code scan bug fixes +- Added new FPGA MMIO API to write 512 bits