From 99928f8a8f671a7e6a139e014f4db55482d09d16 Mon Sep 17 00:00:00 2001
From: Tom Rix <trix@redhat.com>
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 <trix@redhat.com>
---
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 <config.h>
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <time.h>
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <inttypes.h>
+
+#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 <opae/types.h>
+
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <stdio.h>
+
+#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 <opae/types.h>
+#include <stdint.h>
+
+// 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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#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 <options>\n");
+ fprintf(fptr, "\n");
+ fprintf(fptr, "\t-d,--daemon run as daemon process.\n");
+ fprintf(fptr, "\t-l,--logfile <file> the log file for daemon mode [%s].\n", DEFAULT_LOG);
+ fprintf(fptr, "\t-p,--pidfile <file> the pid file for daemon mode [%s].\n", DEFAULT_PID);
+ fprintf(fptr, "\t-s,--socket <sock> the unix domain socket [/tmp/fpga_event_socket].\n");
+ fprintf(fptr, "\t-n,--null-bitstream <file> NULL bitstream (for AP6 handling, may be\n"
+ "\t given multiple times).\n");
+ fprintf(fptr, "\t-c,--config <file> 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <linux/limits.h>
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <sys/types.h>
+#include <pwd.h>
+#include "config_file.h"
+#include "monitored_device.h"
+#include "api/sysfs.h"
+
+#include <json-c/json.h>
+
+#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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <semaphore.h>
+#include <time.h>
+#include <inttypes.h>
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <inttypes.h>
+#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 <opae/types.h>
+
+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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <signal.h>
+#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 <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <opae/fpga.h>
+
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <dlfcn.h>
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <stdio.h>
+#include <linux/limits.h>
+#include <dlfcn.h>
+#include <glob.h>
+
+#include <uuid/uuid.h>
+
+#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 <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <glob.h>
+#include <json-c/json.h>
+
+#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 <config.h>
+#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