diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index d4b5503..a6a0bc9 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 0000000..19d1e7c --- /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 0000000..cdf86c4 --- /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 0000000..b920751 --- /dev/null +++ b/tools/fpgad/api/device_monitoring.c @@ -0,0 +1,73 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include "device_monitoring.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("device_monitoring: " format, ##__VA_ARGS__) + +bool mon_has_error_occurred(fpgad_monitored_device *d, void *err) +{ + unsigned i; + for (i = 0 ; i < d->num_error_occurrences ; ++i) { + if (err == d->error_occurrences[i]) + return true; + } + return false; +} + +bool mon_add_device_error(fpgad_monitored_device *d, void *err) +{ + if (d->num_error_occurrences < + (sizeof(d->error_occurrences) / + sizeof(d->error_occurrences[0]))) { + d->error_occurrences[d->num_error_occurrences++] = err; + return true; + } + LOG("exceeded max number of device errors!\n"); + return false; +} + +void mon_remove_device_error(fpgad_monitored_device *d, void *err) +{ + unsigned i; + unsigned j; + unsigned removed = 0; + for (i = j = 0 ; i < d->num_error_occurrences ; ++i) { + if (d->error_occurrences[i] != err) + d->error_occurrences[j++] = d->error_occurrences[i]; + else + ++removed; + } + d->num_error_occurrences -= removed; +} diff --git a/tools/fpgad/api/device_monitoring.h b/tools/fpgad/api/device_monitoring.h new file mode 100644 index 0000000..c782665 --- /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 0000000..e33eedd --- /dev/null +++ b/tools/fpgad/api/logging.c @@ -0,0 +1,132 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include "logging.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("logging: " format, ##__VA_ARGS__) + +STATIC pthread_mutex_t log_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +STATIC FILE *log_file; + +#define BUF_TIME_LEN 256 + +int log_open(const char *filename) +{ + int res; + int err; + + fpgad_mutex_lock(err, &log_lock); + + log_file = fopen(filename, "a"); + if (log_file) { + time_t raw; + struct tm tm; + char timebuf[BUF_TIME_LEN]; + size_t len; + + time(&raw); + localtime_r(&raw, &tm); + asctime_r(&tm, timebuf); + + len = strnlen(timebuf, sizeof(timebuf)); + if (len < BUF_TIME_LEN) { + timebuf[len - 1] = '\0'; /* erase \n */ + } else { + printf(" Invalid time stamp buffer size \n"); + fpgad_mutex_unlock(err, &log_lock); + return -1; + } + + res = fprintf(log_file, "----- %s -----\n", timebuf); + fflush(log_file); + } else { + res = -1; + } + + fpgad_mutex_unlock(err, &log_lock); + + return res; +} + +int log_printf(const char *fmt, ...) +{ + va_list l; + int res = -1; + int err; + + va_start(l, fmt); + + fpgad_mutex_lock(err, &log_lock); + + if (log_file) { + res = vfprintf(log_file, fmt, l); + fflush(log_file); + } + + fpgad_mutex_unlock(err, &log_lock); + + va_end(l); + + return res; +} + +void log_set(FILE *fptr) +{ + int err; + + fpgad_mutex_lock(err, &log_lock); + + log_close(); + log_file = fptr; + + fpgad_mutex_unlock(err, &log_lock); +} + +void log_close(void) +{ + int err; + + fpgad_mutex_lock(err, &log_lock); + + if (log_file) { + if (log_file != stdout && + log_file != stderr) { + fclose(log_file); + } + log_file = NULL; + } + + fpgad_mutex_unlock(err, &log_lock); +} diff --git a/tools/fpgad/api/logging.h b/tools/fpgad/api/logging.h new file mode 100644 index 0000000..b4e95c1 --- /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 0000000..14a757f --- /dev/null +++ b/tools/fpgad/api/opae_events_api.c @@ -0,0 +1,239 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include + +#include "opae_events_api.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("opae_events_api: " format, ##__VA_ARGS__) + +STATIC pthread_mutex_t list_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +STATIC api_client_event_registry *event_registry_list; + +int opae_api_register_event(int conn_socket, + int fd, + fpga_event_type e, + uint64_t object_id) +{ + api_client_event_registry *r = + (api_client_event_registry *) malloc(sizeof(*r)); + int err; + + if (!r) + return ENOMEM; + + r->conn_socket = conn_socket; + r->fd = fd; + r->data = 1; + r->event = e; + r->object_id = object_id; + + fpgad_mutex_lock(err, &list_lock); + + r->next = event_registry_list; + event_registry_list = r; + + fpgad_mutex_unlock(err, &list_lock); + + return 0; +} + +STATIC void release_event_registry(api_client_event_registry *r) +{ + close(r->fd); + free(r); +} + +int opae_api_unregister_event(int conn_socket, + fpga_event_type e, + uint64_t object_id) +{ + api_client_event_registry *trash; + api_client_event_registry *save; + int err; + int res = 0; + + fpgad_mutex_lock(err, &list_lock); + + trash = event_registry_list; + + if (!trash) { // empty list + res = 1; + goto out_unlock; + } + + if ((conn_socket == trash->conn_socket) && + (e == trash->event) && + (object_id == trash->object_id)) { + + // found at head of list + + event_registry_list = event_registry_list->next; + release_event_registry(trash); + goto out_unlock; + } + + save = trash; + trash = trash->next; + while (trash) { + + if ((conn_socket == trash->conn_socket) && + (e == trash->event) && + (object_id == trash->object_id)) + break; + + save = trash; + trash = trash->next; + } + + if (!trash) { // not found + res = 1; + goto out_unlock; + } + + // found at trash + save->next = trash->next; + release_event_registry(trash); + +out_unlock: + fpgad_mutex_unlock(err, &list_lock); + return res; +} + +STATIC api_client_event_registry * +find_event_for(int conn_socket) +{ + api_client_event_registry *r; + + for (r = event_registry_list ; r ; r = r->next) + if (conn_socket == r->conn_socket) + break; + + return r; +} + +void opae_api_unregister_all_events_for(int conn_socket) +{ + api_client_event_registry *r; + int err; + + fpgad_mutex_lock(err, &list_lock); + + r = find_event_for(conn_socket); + while (r) { + opae_api_unregister_event(conn_socket, r->event, r->object_id); + r = find_event_for(conn_socket); + } + + fpgad_mutex_unlock(err, &list_lock); +} + +void opae_api_unregister_all_events(void) +{ + api_client_event_registry *r; + int err; + + fpgad_mutex_lock(err, &list_lock); + + for (r = event_registry_list ; r != NULL ; ) { + api_client_event_registry *trash; + trash = r; + r = r->next; + release_event_registry(trash); + } + + event_registry_list = NULL; + + fpgad_mutex_unlock(err, &list_lock); +} + +void opae_api_for_each_registered_event +(void (*cb)(api_client_event_registry *r, void *context), +void *context) +{ + api_client_event_registry *r; + int err; + + fpgad_mutex_lock(err, &list_lock); + + for (r = event_registry_list; r != NULL; r = r->next) { + cb(r, context); + } + + fpgad_mutex_unlock(err, &list_lock); +} + +STATIC void check_and_send_EVENT_ERROR(api_client_event_registry *r, + void *context) +{ + fpgad_monitored_device *d = + (fpgad_monitored_device *)context; + + if ((r->event == FPGA_EVENT_ERROR) && + (r->object_id == d->object_id)) { + LOG("object_id: 0x%" PRIx64 " event: FPGA_EVENT_ERROR\n", + d->object_id); + if (write(r->fd, &r->data, sizeof(r->data)) < 0) + LOG("write failed: %s\n", strerror(errno)); + r->data++; + } +} + +void opae_api_send_EVENT_ERROR(fpgad_monitored_device *d) +{ + opae_api_for_each_registered_event(check_and_send_EVENT_ERROR, + d); +} + +STATIC void check_and_send_EVENT_POWER_THERMAL(api_client_event_registry *r, + void *context) +{ + fpgad_monitored_device *d = + (fpgad_monitored_device *)context; + + if ((r->event == FPGA_EVENT_POWER_THERMAL) && + (r->object_id == d->object_id)) { + LOG("object_id: 0x%" PRIx64 " event: FPGA_EVENT_POWER_THERMAL\n", + d->object_id); + if (write(r->fd, &r->data, sizeof(r->data)) < 0) + LOG("write failed: %s\n", strerror(errno)); + r->data++; + } +} + +void opae_api_send_EVENT_POWER_THERMAL(fpgad_monitored_device *d) +{ + opae_api_for_each_registered_event(check_and_send_EVENT_POWER_THERMAL, + d); +} diff --git a/tools/fpgad/api/opae_events_api.h b/tools/fpgad/api/opae_events_api.h new file mode 100644 index 0000000..9e165cc --- /dev/null +++ b/tools/fpgad/api/opae_events_api.h @@ -0,0 +1,85 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef __FPGAD_API_OPAE_EVENTS_API_H__ +#define __FPGAD_API_OPAE_EVENTS_API_H__ + +#ifndef __USE_GNU +#define __USE_GNU +#endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#include "fpgad/fpgad.h" +#include "fpgad/monitored_device.h" + +enum request_type { + REGISTER_EVENT = 0, + UNREGISTER_EVENT +}; + +struct event_request { + enum request_type type; + fpga_event_type event; + uint64_t object_id; +}; + +typedef struct _api_client_event_registry { + int conn_socket; + int fd; + uint64_t data; + fpga_event_type event; + uint64_t object_id; + struct _api_client_event_registry *next; +} api_client_event_registry; + +// 0 on success +int opae_api_register_event(int conn_socket, + int fd, + fpga_event_type e, + uint64_t object_id); + +// 0 on success +int opae_api_unregister_event(int conn_socket, + fpga_event_type e, + uint64_t object_id); + +void opae_api_unregister_all_events_for(int conn_socket); + +void opae_api_unregister_all_events(void); + +void opae_api_for_each_registered_event(void (*cb)(api_client_event_registry *r, + void *context), + void *context); + +void opae_api_send_EVENT_ERROR(fpgad_monitored_device *d); + +void opae_api_send_EVENT_POWER_THERMAL(fpgad_monitored_device *d); + +#endif /* __FPGAD_API_OPAE_EVENTS_API_H__ */ diff --git a/tools/fpgad/api/sysfs.c b/tools/fpgad/api/sysfs.c new file mode 100644 index 0000000..ae8d140 --- /dev/null +++ b/tools/fpgad/api/sysfs.c @@ -0,0 +1,76 @@ +// Copyright(c) 2019-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include + +#include "logging.h" +#include "sysfs.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("sysfs: " format, ##__VA_ARGS__) + +int file_write_string(const char *path, const char *str, size_t len) +{ + FILE *fp; + size_t num; + + fp = fopen(path, "w"); + if (!fp) + return 1; + + num = fwrite(str, 1, len, fp); + + if (!num || ferror(fp)) { + fclose(fp); + return 1; + } + + fclose(fp); + + return 0; +} + +char *cstr_dup(const char *s) +{ + char *p; + size_t len = strnlen(s, 8192); + + p = malloc(len+1); + if (!p) + return NULL; + + memcpy(p, s, len); + p[len] = '\0'; + + return p; +} diff --git a/tools/fpgad/api/sysfs.h b/tools/fpgad/api/sysfs.h new file mode 100644 index 0000000..f8f3b3d --- /dev/null +++ b/tools/fpgad/api/sysfs.h @@ -0,0 +1,45 @@ +// Copyright(c) 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef __FPGAD_API_SYSFS_H__ +#define __FPGAD_API_SYSFS_H__ + +#ifndef __USE_GNU +#define __USE_GNU +#endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +// 0 on success +int file_write_string(const char *path, const char *str, size_t len); + +char *cstr_dup(const char *s); + +#endif /* __FPGAD_API_SYSFS_H__ */ diff --git a/tools/fpgad/command_line.c b/tools/fpgad/command_line.c new file mode 100644 index 0000000..4e0bfb9 --- /dev/null +++ b/tools/fpgad/command_line.c @@ -0,0 +1,516 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#define _GNU_SOURCE +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +#include "command_line.h" +#include "config_file.h" +#include "monitored_device.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("args: " format, ##__VA_ARGS__) + +extern fpgad_supported_device default_supported_devices_table[]; + +#define OPT_STR ":hdl:p:s:n:c:v" + +STATIC struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "daemon", no_argument, NULL, 'd' }, + { "logfile", required_argument, NULL, 'l' }, + { "pidfile", required_argument, NULL, 'p' }, + { "socket", required_argument, NULL, 's' }, + { "null-bitstream", required_argument, NULL, 'n' }, + { "config", required_argument, NULL, 'c' }, + { "version", no_argument, NULL, 'v' }, + + { 0, 0, 0, 0 } +}; + +#define DEFAULT_DIR_ROOT "/var/lib/opae" +#define DEFAULT_DIR_ROOT_SIZE 13 +#define DEFAULT_LOG "fpgad.log" +#define DEFAULT_PID "fpgad.pid" +#define DEFAULT_CFG "fpgad.cfg" + +void cmd_show_help(FILE *fptr) +{ + fprintf(fptr, "Usage: fpgad \n"); + fprintf(fptr, "\n"); + fprintf(fptr, "\t-d,--daemon run as daemon process.\n"); + fprintf(fptr, "\t-l,--logfile the log file for daemon mode [%s].\n", DEFAULT_LOG); + fprintf(fptr, "\t-p,--pidfile the pid file for daemon mode [%s].\n", DEFAULT_PID); + fprintf(fptr, "\t-s,--socket the unix domain socket [/tmp/fpga_event_socket].\n"); + fprintf(fptr, "\t-n,--null-bitstream NULL bitstream (for AP6 handling, may be\n" + "\t given multiple times).\n"); + fprintf(fptr, "\t-c,--config the configuration file [%s].\n", DEFAULT_CFG); + fprintf(fptr, "\t-v,--version display the version and exit.\n"); +} + +STATIC bool cmd_register_null_gbs(struct fpgad_config *c, char *null_gbs_path) +{ + char *canon_path = NULL; + + if (c->num_null_gbs < (sizeof(c->null_gbs) / sizeof(c->null_gbs[0]))) { + canon_path = canonicalize_file_name(null_gbs_path); + + if (canon_path) { + + memset(&c->null_gbs[c->num_null_gbs], 0, + sizeof(opae_bitstream_info)); + + if (opae_load_bitstream(canon_path, + &c->null_gbs[c->num_null_gbs])) { + LOG("failed to load NULL GBS \"%s\"\n", canon_path); + opae_unload_bitstream(&c->null_gbs[c->num_null_gbs]); + free(canon_path); + return false; + } + + c->num_null_gbs++; + + LOG("registering NULL bitstream \"%s\"\n", canon_path); + + } else { + LOG("error with NULL GBS argument: %s\n", strerror(errno)); + return false; + } + + } else { + LOG("maximum number of NULL bitstreams exceeded. Ignoring -n option.\n"); + } + return true; +} + +int cmd_parse_args(struct fpgad_config *c, int argc, char *argv[]) +{ + int getopt_ret; + int option_index; + size_t len; + + while (-1 != (getopt_ret = getopt_long(argc, argv, OPT_STR, longopts, &option_index))) { + const char *tmp_optarg = optarg; + + if (optarg && ('=' == *tmp_optarg)) + ++tmp_optarg; + + if (!optarg && (optind < argc) && + (NULL != argv[optind]) && + ('-' != argv[optind][0])) + tmp_optarg = argv[optind++]; + + switch (getopt_ret) { + case 'h': + cmd_show_help(stdout); + return -2; + break; + + case 'd': + c->daemon = 1; + LOG("daemon requested\n"); + break; + + case 'l': + if (tmp_optarg) { + len = strnlen(tmp_optarg, PATH_MAX - 1); + memcpy(c->logfile, tmp_optarg, len); + c->logfile[len] = '\0'; + } else { + LOG("missing logfile parameter.\n"); + return 1; + } + break; + + case 'p': + if (tmp_optarg) { + len = strnlen(tmp_optarg, PATH_MAX - 1); + memcpy(c->pidfile, tmp_optarg, len); + c->pidfile[len] = '\0'; + } else { + LOG("missing pidfile parameter.\n"); + return 1; + } + break; + + case 'n': + if (tmp_optarg) { + if (!cmd_register_null_gbs(c, (char *)tmp_optarg)) { + LOG("invalid null gbs path: \"%s\"\n", tmp_optarg); + return 1; + } + } else { + LOG("missing bitstream parameter.\n"); + return 1; + } + break; + + case 's': + if (tmp_optarg) { + c->api_socket = tmp_optarg; + LOG("daemon socket is %s\n", c->api_socket); + } else { + LOG("missing socket parameter.\n"); + return 1; + } + break; + + case 'c': + if (tmp_optarg) { + len = strnlen(tmp_optarg, PATH_MAX - 1); + memcpy(c->cfgfile, tmp_optarg, len); + c->cfgfile[len] = '\0'; + } else { + LOG("missing cfgfile parameter.\n"); + return 1; + } + break; + + case 'v': + fprintf(stdout, "fpgad %s %s%s\n", + OPAE_VERSION, + OPAE_GIT_COMMIT_HASH, + OPAE_GIT_SRC_TREE_DIRTY ? "*":""); + return -2; + break; + + case ':': + LOG("Missing option argument.\n"); + return 1; + + case '?': + LOG("Invalid command option.\n"); + return 1; + + default: + LOG("Invalid command option.\n"); + return 1; + } + + } + + return 0; +} + +int cmd_canonicalize_paths(struct fpgad_config *c) +{ + char *sub; + bool def; + mode_t mode; + struct stat stat_buf; + bool search = true; + char buf[PATH_MAX]; + char *canon_path; + uid_t uid; + size_t len; + + uid = geteuid(); + + if (!uid) { + // If we're being run as root, then use DEFAULT_DIR_ROOT + // as the working directory. + memcpy(c->directory, DEFAULT_DIR_ROOT, sizeof(DEFAULT_DIR_ROOT)); + c->directory[sizeof(DEFAULT_DIR_ROOT)] = '\0'; + mode = 0755; + c->filemode = 0026; + } else { + // We're not root. Try to use ${HOME}/.opae + struct passwd *passwd; + + passwd = getpwuid(uid); + + canon_path = canonicalize_file_name(passwd->pw_dir); + + if (canon_path) { + snprintf(c->directory, sizeof(c->directory), + "%s/.opae", canon_path); + free(canon_path); + } else { + // ${HOME} not found or invalid - use current dir. + if (getcwd(buf, sizeof(buf))) { + if (snprintf(c->directory, sizeof(c->directory), + "%s/.opae", buf) < 0) { + len = strnlen("./.opae", + sizeof(c->directory) - 1); + memcpy(c->directory, "./.opae", len); + c->directory[len] = '\0'; + } + } else { + // Current directory not found - use / + len = strnlen("/.opae", sizeof(c->directory) - 1); + memcpy(c->directory, "/.opae", len); + c->directory[len] = '\0'; + } + } + + mode = 0775; + c->filemode = 0022; + } + + if (cmd_path_is_symlink(c->directory)) { + LOG("Aborting - working directory contains a link: %s\n.", + c->directory); + return 1; + } + LOG("daemon working directory is %s\n", c->directory); + + // Create the directory if it doesn't exist. + if (lstat(c->directory, &stat_buf) && (errno == ENOENT)) { + if (mkdir(c->directory, mode)) { + LOG("mkdir failed\n"); + return 1; + } + } + + // Verify logfile and pidfile do not contain ".." + // nor "/". + def = false; + sub = strstr(c->logfile, ".."); + if (sub) + def = true; + + sub = strstr(c->logfile, "/"); + if (sub) + def = true; + + if (def || (c->logfile[0] == '\0')) { + if (snprintf(c->logfile, sizeof(c->logfile), + "%s/%s", c->directory, DEFAULT_LOG) < 0) { + len = strnlen("./" DEFAULT_LOG, + sizeof(c->logfile) - 1); + memcpy(c->logfile, "./" DEFAULT_LOG, len); + c->logfile[len] = '\0'; + } + } else { + len = strnlen(c->logfile, sizeof(buf) - 1); + memcpy(buf, c->logfile, len); + buf[len] = '\0'; + + if (snprintf(c->logfile, sizeof(c->logfile), + "%s/%s", c->directory, buf) < 0) { + len = strnlen("./" DEFAULT_LOG, + sizeof(c->logfile) - 1); + memcpy(c->logfile, "./" DEFAULT_LOG, len); + c->logfile[len] = '\0'; + } + } + + if (cmd_path_is_symlink(c->logfile)) { + LOG("Aborting - log file path contains a link: %s\n.", + c->logfile); + return 1; + } + LOG("daemon log file is %s\n", c->logfile); + + def = false; + sub = strstr(c->pidfile, ".."); + if (sub) + def = true; + + sub = strstr(c->pidfile, "/"); + if (sub) + def = true; + + if (def || (c->pidfile[0] == '\0')) { + + if (snprintf(c->pidfile, sizeof(c->pidfile), + "%s/%s", c->directory, DEFAULT_PID) < 0) { + len = strnlen("./" DEFAULT_PID, + sizeof(c->pidfile) - 1); + memcpy(c->pidfile, "./" DEFAULT_PID, len); + c->pidfile[len] = '\0'; + } + + } else { + len = strnlen(c->pidfile, sizeof(buf) - 1); + memcpy(buf, c->pidfile, len); + buf[len] = '\0'; + + if (snprintf(c->pidfile, sizeof(c->pidfile), + "%s/%s", c->directory, buf) < 0) { + len = strnlen("./" DEFAULT_PID, + sizeof(c->pidfile) - 1); + memcpy(c->pidfile, "./" DEFAULT_PID, len); + c->pidfile[len] = '\0'; + } + } + + if (cmd_path_is_symlink(c->pidfile)) { + LOG("Aborting - pid file path contains a link: %s\n.", + c->pidfile); + return 1; + } + LOG("daemon pid file is %s\n", c->pidfile); + + // Verify cfgfile doesn't contain ".." + def = false; + sub = strstr(c->cfgfile, ".."); + if (sub) + def = true; + + if (def || (c->cfgfile[0] == '\0')) { + search = true; + } else { + canon_path = canonicalize_file_name(c->cfgfile); + if (canon_path) { + + if (!cmd_path_is_symlink(c->cfgfile)) { + + len = strnlen(canon_path, + sizeof(c->cfgfile) - 1); + memcpy(c->cfgfile, + canon_path, + len); + c->cfgfile[len] = '\0'; + + if (!cfg_load_config(c)) { + LOG("daemon cfg file is %s\n", + c->cfgfile); + search = false; // found and loaded it + } + + } + + free(canon_path); + } + } + + if (search) { + c->cfgfile[0] = '\0'; + if (cfg_find_config_file(c)) + LOG("failed to find config file.\n"); + else { + if (cfg_load_config(c)) + LOG("failed to load config file %s\n", + c->cfgfile); + else + LOG("daemon cfg file is %s\n", c->cfgfile); + } + } + + if (!c->supported_devices) { + LOG("using default configuration.\n"); + c->cfgfile[0] = '\0'; + c->supported_devices = default_supported_devices_table; + } + + return 0; +} + +void cmd_destroy(struct fpgad_config *c) +{ + unsigned i; + + if (c->daemon) + unlink(c->pidfile); + + for (i = 0 ; i < c->num_null_gbs ; ++i) { + if (c->null_gbs[i].filename) + free((char *)c->null_gbs[i].filename); + opae_unload_bitstream(&c->null_gbs[i]); + } + c->num_null_gbs = 0; + + if (c->supported_devices && + (c->supported_devices != default_supported_devices_table)) { + + for (i = 0 ; c->supported_devices[i].library_path ; ++i) { + fpgad_supported_device *d = &c->supported_devices[i]; + if (d->library_path) + free((void *)d->library_path); + if (d->config) + free((void *)d->config); + } + + free(c->supported_devices); + } + c->supported_devices = NULL; +} + +bool cmd_path_is_symlink(const char *path) +{ + char component[PATH_MAX]; + struct stat stat_buf; + size_t len; + char *pslash; + + len = strnlen(path, PATH_MAX - 1); + if (!len) // empty path + return false; + + memcpy(component, path, len); + component[len] = '\0'; + + if (component[0] == '/') { + // absolute path + + pslash = realpath(path, component); + + if (strcmp(component, path)) + return true; + + + } else { + // relative path + + pslash = strrchr(component, '/'); + + while (pslash) { + + if (fstatat(AT_FDCWD, component, + &stat_buf, AT_SYMLINK_NOFOLLOW)) { + return false; + } + + if (S_ISLNK(stat_buf.st_mode)) + return true; + + *pslash = '\0'; + pslash = strrchr(component, '/'); + } + + if (fstatat(AT_FDCWD, component, + &stat_buf, AT_SYMLINK_NOFOLLOW)) { + return false; + } + + if (S_ISLNK(stat_buf.st_mode)) + return true; + + } + + return false; +} diff --git a/tools/fpgad/command_line.h b/tools/fpgad/command_line.h new file mode 100644 index 0000000..99aec65 --- /dev/null +++ b/tools/fpgad/command_line.h @@ -0,0 +1,84 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef __FPGAD_COMMAND_LINE_H__ +#define __FPGAD_COMMAND_LINE_H__ + +#include +#include +#include +#include +#include "bitstream.h" + +struct _fpgad_supported_device; + +#define MAX_NULL_GBS 32 + +struct fpgad_config { + useconds_t poll_interval_usec; + + bool daemon; + char directory[PATH_MAX]; + char logfile[PATH_MAX]; + char pidfile[PATH_MAX]; + char cfgfile[PATH_MAX]; + mode_t filemode; + + bool running; + + const char *api_socket; + + opae_bitstream_info null_gbs[MAX_NULL_GBS]; + unsigned num_null_gbs; + + pthread_t bmc_monitor_thr; + pthread_t monitor_thr; + pthread_t event_dispatcher_thr; + pthread_t events_api_thr; + + struct _fpgad_supported_device *supported_devices; +}; + +extern struct fpgad_config global_config; + +/* +** Returns +** -2 if --help requested +** -1 on parse error +** 0 on success +*/ +int cmd_parse_args(struct fpgad_config *c, int argc, char *argv[]); + +void cmd_show_help(FILE *fptr); + +// 0 on success +int cmd_canonicalize_paths(struct fpgad_config *c); + +void cmd_destroy(struct fpgad_config *c); + +bool cmd_path_is_symlink(const char *path); + +#endif /* __FPGAD_COMMAND_LINE_H__ */ diff --git a/tools/fpgad/config_file.c b/tools/fpgad/config_file.c new file mode 100644 index 0000000..2172d2f --- /dev/null +++ b/tools/fpgad/config_file.c @@ -0,0 +1,621 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include "config_file.h" +#include "monitored_device.h" +#include "api/sysfs.h" + +#include + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("cfg: " format, ##__VA_ARGS__) + +#define CFG_TRY_FILE(__f) \ +do { \ + canon = canonicalize_file_name(__f); \ + if (canon) { \ + \ + if (!cmd_path_is_symlink(__f)) { \ + size_t len = strnlen(canon, \ + sizeof(c->cfgfile) - 1); \ + memcpy(c->cfgfile, \ + canon, \ + len); \ + c->cfgfile[len] = '\0'; \ + free(canon); \ + return 0; \ + } \ + \ + free(canon); \ + } \ +} while (0) + +int cfg_find_config_file(struct fpgad_config *c) +{ + char path[PATH_MAX]; + char *e; + char *canon = NULL; + uid_t uid; + size_t len; + + uid = geteuid(); + + e = getenv("FPGAD_CONFIG_FILE"); + if (e) { + // try $FPGAD_CONFIG_FILE + len = strnlen(e, sizeof(path) - 1); + memcpy(path, e, len); + path[len] = '\0'; + + CFG_TRY_FILE(path); + } + + if (!uid) { + CFG_TRY_FILE("/var/lib/opae/fpgad.cfg"); + } else { + struct passwd *passwd; + + passwd = getpwuid(uid); + + // try $HOME/.opae/fpgad.cfg + snprintf(path, sizeof(path), + "%s/.opae/fpgad.cfg", passwd->pw_dir); + + CFG_TRY_FILE(path); + } + + CFG_TRY_FILE("/etc/opae/fpgad.cfg"); + + return 1; // not found +} + +STATIC char *cfg_read_file(const char *file) +{ + FILE *fp; + size_t len; + char *buf; + + fp = fopen(file, "r"); + if (!fp) { + LOG("fopen failed.\n"); + return NULL; + } + + if (fseek(fp, 0, SEEK_END)) { + LOG("fseek failed.\n"); + fclose(fp); + return NULL; + } + + len = (size_t)ftell(fp); + ++len; // for \0 + + if (len == 1) { + LOG("%s is empty.\n", file); + fclose(fp); + return NULL; + } + + if (fseek(fp, 0, SEEK_SET)) { + LOG("fseek failed.\n"); + fclose(fp); + return NULL; + } + + buf = (char *)malloc(len); + if (!buf) { + LOG("malloc failed.\n"); + fclose(fp); + return NULL; + } + + if ((fread(buf, 1, len - 1, fp) != len - 1) || + ferror(fp)) { + LOG("fread failed.\n"); + fclose(fp); + free(buf); + return NULL; + } + + fclose(fp); + buf[len - 1] = '\0'; + + return buf; +} + +typedef struct _cfg_vendor_device_id { + uint16_t vendor_id; + uint16_t device_id; + struct _cfg_vendor_device_id *next; +} cfg_vendor_device_id; + +typedef struct _cfg_plugin_configuration { + char *configuration; + bool enabled; + char *library; + cfg_vendor_device_id *devices; + struct _cfg_plugin_configuration *next; +} cfg_plugin_configuration; + +STATIC cfg_vendor_device_id *alloc_device(uint16_t vendor_id, + uint16_t device_id) +{ + cfg_vendor_device_id *p; + + p = (cfg_vendor_device_id *)malloc(sizeof(cfg_vendor_device_id)); + if (p) { + p->vendor_id = vendor_id; + p->device_id = device_id; + p->next = NULL; + } + + return p; +} + +STATIC cfg_plugin_configuration *alloc_configuration(char *configuration, + bool enabled, + char *library, + cfg_vendor_device_id *devs) +{ + cfg_plugin_configuration *p; + + p = (cfg_plugin_configuration *) + malloc(sizeof(cfg_plugin_configuration)); + if (p) { + p->configuration = configuration; + p->enabled = enabled; + p->library = library; + p->devices = devs; + p->next = NULL; + } + + return p; +} + +STATIC cfg_vendor_device_id * +cfg_process_plugin_devices(const char *name, + json_object *j_devices) +{ + int i; + int devs; + cfg_vendor_device_id *head = NULL; + cfg_vendor_device_id *id = NULL; + uint16_t vendor_id; + uint16_t device_id; + const char *s; + char *endptr; + + if (!json_object_is_type(j_devices, json_type_array)) { + LOG("'devices' JSON object not array.\n"); + return NULL; + } + + devs = json_object_array_length(j_devices); + for (i = 0 ; i < devs ; ++i) { + json_object *j_dev = json_object_array_get_idx(j_devices, i); + json_object *j_vid; + json_object *j_did; + + if (!json_object_is_type(j_dev, json_type_array)) { + LOG("%s 'devices' entry %d not array.\n", + name, i); + goto out_free; + } + + if (json_object_array_length(j_dev) != 2) { + LOG("%s 'devices' entry %d not array[2].\n", + name, i); + goto out_free; + } + + j_vid = json_object_array_get_idx(j_dev, 0); + if (json_object_is_type(j_vid, json_type_string)) { + s = json_object_get_string(j_vid); + endptr = NULL; + + vendor_id = (uint16_t)strtoul(s, &endptr, 0); + if (*endptr != '\0') { + LOG("%s malformed Vendor ID at devices[%d]\n", + name, i); + goto out_free; + } + + } else if (json_object_is_type(j_vid, json_type_int)) { + vendor_id = (uint16_t)json_object_get_int(j_vid); + } else { + LOG("%s invalid Vendor ID at devices[%d]\n", + name, i); + goto out_free; + } + + j_did = json_object_array_get_idx(j_dev, 1); + if (json_object_is_type(j_did, json_type_string)) { + s = json_object_get_string(j_did); + endptr = NULL; + + device_id = (uint16_t)strtoul(s, &endptr, 0); + if (*endptr != '\0') { + LOG("%s malformed Device ID at devices[%d]\n", + name, i); + goto out_free; + } + + } else if (json_object_is_type(j_did, json_type_int)) { + device_id = (uint16_t)json_object_get_int(j_did); + } else { + LOG("%s invalid Device ID at devices[%d]\n", + name, i); + goto out_free; + } + + if (!head) { + head = alloc_device(vendor_id, device_id); + if (!head) { + LOG("malloc failed.\n"); + goto out_free; + } + + id = head; + } else { + id->next = alloc_device(vendor_id, device_id); + if (!id->next) { + LOG("malloc failed.\n"); + goto out_free; + } + + id = id->next; + } + } + + return head; + +out_free: + for (id = head ; id ; ) { + cfg_vendor_device_id *trash = id; + id = id->next; + free(trash); + } + return NULL; +} + +STATIC int cfg_process_plugin(const char *name, + json_object *j_configurations, + cfg_plugin_configuration **list) +{ + json_object *j_cfg_plugin = NULL; + json_object *j_cfg_plugin_configuration = NULL; + json_object *j_enabled = NULL; + json_object *j_plugin = NULL; + json_object *j_devices = NULL; + char *configuration = NULL; + bool enabled = false; + char *plugin = NULL; + cfg_plugin_configuration *c = NULL; + + if (!json_object_object_get_ex(j_configurations, + name, + &j_cfg_plugin)) { + LOG("couldn't find configurations section" + " for %s.\n", name); + return 1; + } + + if (!json_object_object_get_ex(j_cfg_plugin, + "configuration", + &j_cfg_plugin_configuration)) { + LOG("couldn't find %s configuration section.\n", name); + return 1; + } + + configuration = (char *)json_object_to_json_string_ext( + j_cfg_plugin_configuration, + JSON_C_TO_STRING_PLAIN); + if (!configuration) { + LOG("failed to parse configuration for %s.\n", name); + return 1; + } + + configuration = cstr_dup(configuration); + if (!configuration) { + LOG("cstr_dup failed.\n"); + return 1; + } + + if (!json_object_object_get_ex(j_cfg_plugin, + "enabled", + &j_enabled)) { + LOG("couldn't find enabled key" + " for %s.\n", name); + goto out_free; + } + + if (!json_object_is_type(j_enabled, json_type_boolean)) { + LOG("enabled key for %s not boolean.\n", name); + goto out_free; + } + + enabled = json_object_get_boolean(j_enabled); + + if (!json_object_object_get_ex(j_cfg_plugin, + "plugin", + &j_plugin)) { + LOG("couldn't find plugin key" + " for %s.\n", name); + goto out_free; + } + + if (!json_object_is_type(j_plugin, json_type_string)) { + LOG("plugin key for %s not string.\n", name); + goto out_free; + } + + plugin = cstr_dup(json_object_get_string(j_plugin)); + if (!plugin) { + LOG("cstr_dup failed.\n"); + goto out_free; + } + + if (!json_object_object_get_ex(j_cfg_plugin, + "devices", + &j_devices)) { + LOG("couldn't find devices key" + " for %s.\n", name); + goto out_free; + } + + if (!(*list)) { // list is empty + c = alloc_configuration(configuration, + enabled, + plugin, + NULL); + if (!c) { + LOG("malloc failed.\n"); + goto out_free; + } + + *list = c; + } else { + for (c = *list ; c->next ; c = c->next) + /* find the end of the list */ ; + + c->next = alloc_configuration(configuration, + enabled, + plugin, + NULL); + if (!c->next) { + LOG("malloc failed.\n"); + goto out_free; + } + + c = c->next; + } + + c->devices = cfg_process_plugin_devices(name, j_devices); + + return 0; + +out_free: + if (configuration) + free(configuration); + if (plugin) + free(plugin); + if (c) + free(c); + return 1; +} + +STATIC fpgad_supported_device * +cfg_json_to_supported(cfg_plugin_configuration *configurations) +{ + cfg_plugin_configuration *c; + cfg_vendor_device_id *d; + size_t num_devices = 0; + fpgad_supported_device *supported; + int i; + + // find the number of devices + for (c = configurations ; c ; c = c->next) { + if (!c->enabled) // skip it + continue; + for (d = c->devices ; d ; d = d->next) { + ++num_devices; + } + } + + ++num_devices; // +1 for NULL terminator + + supported = calloc(num_devices, sizeof(fpgad_supported_device)); + if (!supported) { + LOG("calloc failed.\n"); + return NULL; + } + + i = 0; + for (c = configurations ; c ; c = c->next) { + if (!c->enabled) // skip it + continue; + for (d = c->devices ; d ; d = d->next) { + fpgad_supported_device *dev = &supported[i++]; + + dev->vendor_id = d->vendor_id; + dev->device_id = d->device_id; + dev->library_path = cstr_dup(c->library); + dev->config = cstr_dup(c->configuration); + } + } + + for (c = configurations ; c ; ) { + cfg_plugin_configuration *ctrash = c; + + for (d = c->devices ; d ; ) { + cfg_vendor_device_id *dtrash = d; + d = d->next; + free(dtrash); + } + + c = c->next; + + if (ctrash->configuration) + free(ctrash->configuration); + if (ctrash->library) + free(ctrash->library); + free(ctrash); + } + + return supported; +} + +STATIC bool cfg_verify_supported_devices(fpgad_supported_device *d) +{ + while (d->library_path) { + char *sub = NULL; + + if (d->library_path[0] == '/') { + LOG("plugin library paths may not " + "be absolute paths: %s\n", d->library_path); + return false; + } + + if (cmd_path_is_symlink(d->library_path)) { + LOG("plugin library paths may not " + "contain links: %s\n", d->library_path); + return false; + } + + sub = strstr((char *)d->library_path, ".."); + if (sub) { + LOG("plugin library paths may not " + "contain .. : %s\n", d->library_path); + return false; + } + + ++d; + } + + return true; +} + +int cfg_load_config(struct fpgad_config *c) +{ + char *cfg_buf; + json_object *root = NULL; + json_object *j_configurations = NULL; + json_object *j_plugins = NULL; + enum json_tokener_error j_err = json_tokener_success; + int res = 1; + int num_plugins; + int i; + cfg_plugin_configuration *configurations = NULL; + + cfg_buf = cfg_read_file(c->cfgfile); + if (!cfg_buf) + return res; + + root = json_tokener_parse_verbose(cfg_buf, &j_err); + if (!root) { + LOG("error parsing %s: %s\n", + c->cfgfile, + json_tokener_error_desc(j_err)); + goto out_free; + } + + if (!json_object_object_get_ex(root, + "configurations", + &j_configurations)) { + LOG("failed to find configurations section in %s.\n", + c->cfgfile); + goto out_put; + } + + if (!json_object_object_get_ex(root, "plugins", &j_plugins)) { + LOG("failed to find plugins section in %s.\n", c->cfgfile); + goto out_put; + } + + if (!json_object_is_type(j_plugins, json_type_array)) { + LOG("'plugins' JSON object not array.\n"); + goto out_put; + } + + num_plugins = json_object_array_length(j_plugins); + for (i = 0 ; i < num_plugins ; ++i) { + json_object *j_plugin; + const char *plugin_name; + + j_plugin = json_object_array_get_idx(j_plugins, i); + plugin_name = json_object_get_string(j_plugin); + + if (cfg_process_plugin(plugin_name, + j_configurations, + &configurations)) + goto out_put; + } + + if (!configurations) { + LOG("no configurations found in %s.\n", c->cfgfile); + goto out_put; + } + + c->supported_devices = cfg_json_to_supported(configurations); + + if (c->supported_devices) { + + if (cfg_verify_supported_devices(c->supported_devices)) { + res = 0; + } else { + fpgad_supported_device *trash = c->supported_devices; + + LOG("invalid configuration file\n"); + + while (trash->library_path) { + free((void *)trash->library_path); + if (trash->config) + free((void *)trash->config); + + ++trash; + } + + free(c->supported_devices); + c->supported_devices = NULL; + } + + } + +out_put: + json_object_put(root); +out_free: + free(cfg_buf); + return res; +} diff --git a/tools/fpgad/config_file.h b/tools/fpgad/config_file.h new file mode 100644 index 0000000..0e476a3 --- /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 0000000..79f05e5 --- /dev/null +++ b/tools/fpgad/daemonize.c @@ -0,0 +1,98 @@ +// Copyright(c) 2017-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/* + * daemonize.c : routine to become a system daemon process. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int daemonize(void (*hndlr)(int, siginfo_t *, void *), mode_t mask, const char *dir) +{ + pid_t pid; + pid_t sid; + int res; + int fd; + struct sigaction sa; + + pid = fork(); + if (pid < 0) // fork() failed. + return errno; + + // 1) Orphan the child process so that it runs in the background. + if (pid > 0) + exit(0); + + // 2) Become leader of a new session and process group leader of new process + // group. The process is now detached from its controlling terminal. + sid = setsid(); + if (sid < 0) + return errno; + + // 3) Establish signal handler. + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; + sa.sa_sigaction = hndlr; + + res = sigaction(SIGINT, &sa, NULL); + if (res < 0) + return errno; + + res = sigaction(SIGTERM, &sa, NULL); + if (res < 0) + return errno; + + // 4) Orphan the child again - the session leading process terminates. + // (only session leaders can request TTY). + pid = fork(); + if (pid < 0) // fork() failed. + return errno; + + if (pid > 0) + exit(0); + + // 5) Set new file mode mask. + umask(mask); + + // 6) change directory + res = chdir(dir); + if (res < 0) + return errno; + + // 7) Close all open file descriptors + fd = sysconf(_SC_OPEN_MAX); + while (fd >= 0) + close(fd--); + + return 0; +} diff --git a/tools/fpgad/event_dispatcher_thread.c b/tools/fpgad/event_dispatcher_thread.c new file mode 100644 index 0000000..235a326 --- /dev/null +++ b/tools/fpgad/event_dispatcher_thread.c @@ -0,0 +1,266 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include "event_dispatcher_thread.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("event_dispatcher_thread: " format, ##__VA_ARGS__) + +event_dispatcher_thread_config event_dispatcher_config = { + .global = &global_config, + .sched_policy = SCHED_RR, + .sched_priority = 30, +}; + +#define EVENT_DISPATCH_QUEUE_DEPTH 512 + +typedef struct _evt_dispatch_queue { + event_dispatch_queue_item q[EVENT_DISPATCH_QUEUE_DEPTH]; + unsigned head; + unsigned tail; + pthread_mutex_t lock; +} evt_dispatch_queue; + +STATIC sem_t evt_dispatch_sem; + +STATIC evt_dispatch_queue normal_queue = { + { { NULL, NULL, NULL }, }, + 0, + 0, + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +}; + +STATIC evt_dispatch_queue high_priority_queue = { + { { NULL, NULL, NULL }, }, + 0, + 0, + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +}; + +STATIC void evt_queue_init(evt_dispatch_queue *q) +{ + memset(q->q, 0, sizeof(q->q)); + q->head = q->tail = 0; +} + +STATIC void evt_queue_destroy(evt_dispatch_queue *q) +{ + q->head = q->tail = 0; +} + +STATIC volatile bool dispatcher_is_ready = false; + +bool evt_dispatcher_is_ready(void) +{ + return dispatcher_is_ready; +} + +STATIC bool evt_queue_is_full(evt_dispatch_queue *q) +{ + const size_t num = sizeof(q->q) / sizeof(q->q[0]); + + if (q->tail > q->head) { + if ((q->head == 0) && (q->tail == (num - 1))) + return true; + } else if (q->tail < q->head) { + if (q->tail == (q->head - 1)) + return true; + } + return false; +} + +STATIC bool evt_queue_is_empty(evt_dispatch_queue *q) +{ + return q->head == q->tail; +} + +STATIC bool _evt_queue_response(evt_dispatch_queue *q, + fpgad_respond_event_t callback, + fpgad_monitored_device *device, + void *context) +{ + int res; + + opae_mutex_lock(res, &q->lock); + + if (evt_queue_is_full(q)) { + opae_mutex_unlock(res, &q->lock); + return false; + } + + q->q[q->tail].callback = callback; + q->q[q->tail].device = device; + q->q[q->tail].context = context; + + q->tail = (q->tail + 1) % EVENT_DISPATCH_QUEUE_DEPTH; + + opae_mutex_unlock(res, &q->lock); + + sem_post(&evt_dispatch_sem); + + return true; +} + +STATIC bool _evt_queue_get(evt_dispatch_queue *q, + event_dispatch_queue_item *item) +{ + int res; + + opae_mutex_lock(res, &q->lock); + + if (evt_queue_is_empty(q)) { + opae_mutex_unlock(res, &q->lock); + return false; + } + + *item = q->q[q->head]; + memset(&q->q[q->head], 0, sizeof(q->q[0])); + q->head = (q->head + 1) % EVENT_DISPATCH_QUEUE_DEPTH; + + opae_mutex_unlock(res, &q->lock); + + return true; +} + +bool evt_queue_response(fpgad_respond_event_t callback, + fpgad_monitored_device *device, + void *context) +{ + return _evt_queue_response(&normal_queue, + callback, + device, + context); +} + +bool evt_queue_get(event_dispatch_queue_item *item) +{ + return _evt_queue_get(&normal_queue, item); +} + +bool evt_queue_response_high(fpgad_respond_event_t callback, + fpgad_monitored_device *device, + void *context) +{ + return _evt_queue_response(&high_priority_queue, + callback, + device, + context); +} + +bool evt_queue_get_high(event_dispatch_queue_item *item) +{ + return _evt_queue_get(&high_priority_queue, item); +} + +void *event_dispatcher_thread(void *thread_context) +{ + event_dispatcher_thread_config *c = + (event_dispatcher_thread_config *)thread_context; + struct sched_param sched_param; + int policy = 0; + int res; + struct timespec ts; + + LOG("starting\n"); + + res = pthread_getschedparam(pthread_self(), &policy, &sched_param); + if (res) { + LOG("error getting scheduler params: %s\n", strerror(res)); + } else { + policy = c->sched_policy; + sched_param.sched_priority = c->sched_priority; + + res = pthread_setschedparam(pthread_self(), + policy, + &sched_param); + if (res) { + LOG("error setting scheduler params" + " (got root?): %s\n", strerror(res)); + } + } + + evt_queue_init(&normal_queue); + evt_queue_init(&high_priority_queue); + + if (sem_init(&evt_dispatch_sem, 0, 0)) { + LOG("failed to init queue sem.\n"); + goto out_exit; + } + + dispatcher_is_ready = true; + + while (c->global->running) { + + clock_gettime(CLOCK_REALTIME, &ts); + + ts.tv_nsec += c->global->poll_interval_usec * 1000; + if (ts.tv_nsec > 1000000000) { + ++ts.tv_sec; + ts.tv_nsec -= 1000000000; + } + + res = sem_timedwait(&evt_dispatch_sem, &ts); + + if (!res) { + event_dispatch_queue_item item; + + // Process all high-priority items first + while (evt_queue_get_high(&item)) { + LOG("dispatching (high) for object_id: 0x%" PRIx64 ".\n", + item.device->object_id); + item.callback(item.device, item.context); + } + + if (evt_queue_get(&item)) { + LOG("dispatching for object_id: 0x%" PRIx64 ".\n", + item.device->object_id); + item.callback(item.device, item.context); + } + } + + } + + dispatcher_is_ready = false; + + evt_queue_destroy(&normal_queue); + evt_queue_destroy(&high_priority_queue); + + sem_destroy(&evt_dispatch_sem); + +out_exit: + LOG("exiting\n"); + return NULL; +} diff --git a/tools/fpgad/event_dispatcher_thread.h b/tools/fpgad/event_dispatcher_thread.h new file mode 100644 index 0000000..5fdff07 --- /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 0000000..19a9e13 --- /dev/null +++ b/tools/fpgad/events_api_thread.c @@ -0,0 +1,296 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_GNU +#define __USE_GNU +#endif + +#include +#include +#include +#include +#include "events_api_thread.h" +#include "api/opae_events_api.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("events_api_thread: " format, ##__VA_ARGS__) + +events_api_thread_config events_api_config = { + .global = &global_config, + .sched_policy = SCHED_RR, + .sched_priority = 10, +}; + +#define MAX_CLIENT_CONNECTIONS 1023 +#define SRV_SOCKET 0 +#define FIRST_CLIENT_SOCKET 1 + +/* array keeping track of all connection file descriptors (plus server socket) */ +STATIC struct pollfd pollfds[MAX_CLIENT_CONNECTIONS+1]; +STATIC nfds_t num_fds = 1; + +STATIC void remove_client(int conn_socket) +{ + nfds_t i, j; + nfds_t removed = 0; + + opae_api_unregister_all_events_for(conn_socket); + LOG("closing connection conn_socket=%d.\n", conn_socket); + close(conn_socket); + + for (i = j = FIRST_CLIENT_SOCKET ; i < num_fds ; ++i) { + if (conn_socket != pollfds[i].fd) { + if (j != i) + pollfds[j] = pollfds[i]; + ++j; + } else { + ++removed; + } + } + + num_fds -= removed; +} + +STATIC int handle_message(int conn_socket) +{ + struct msghdr mh; + struct cmsghdr *cmh; + struct iovec iov[1]; + struct event_request req; + char buf[CMSG_SPACE(sizeof(int))]; + ssize_t n; + int *fd_ptr; + + /* set up ancillary data message header */ + iov[0].iov_base = &req; + iov[0].iov_len = sizeof(req); + memset(buf, 0, sizeof(buf)); + mh.msg_name = NULL; + mh.msg_namelen = 0; + mh.msg_iov = iov; + mh.msg_iovlen = sizeof(iov) / sizeof(iov[0]); + mh.msg_control = buf; + mh.msg_controllen = CMSG_LEN(sizeof(int)); + mh.msg_flags = 0; + cmh = CMSG_FIRSTHDR(&mh); + cmh->cmsg_len = CMSG_LEN(sizeof(int)); + cmh->cmsg_level = SOL_SOCKET; + cmh->cmsg_type = SCM_RIGHTS; + + n = recvmsg(conn_socket, &mh, 0); + if (n < 0) { + LOG("recvmsg() failed: %s\n", strerror(errno)); + return (int)n; + } + + if (!n) { // socket closed by peer + remove_client(conn_socket); + return (int)n; + } + + switch (req.type) { + + case REGISTER_EVENT: + fd_ptr = (int *)CMSG_DATA(cmh); + + if (opae_api_register_event(conn_socket, *fd_ptr, + req.event, req.object_id)) { + LOG("failed to register event\n"); + return -1; + } + + LOG("registered event sock=%d:fd=%d" + "(event=%d object_id=0x%" PRIx64 ")\n", + conn_socket, *fd_ptr, req.event, req.object_id); + + break; + + case UNREGISTER_EVENT: + + if (opae_api_unregister_event(conn_socket, + req.event, + req.object_id)) { + LOG("failed to unregister event\n"); + return -1; + } + + LOG("unregistered event sock=%d:" + "(event=%d object_id=0x%" PRIx64 ")\n", + conn_socket, req.event, req.object_id); + + break; + + default: + LOG("unknown request type %d\n", req.type); + return -1; + } + + return 0; +} + +STATIC volatile bool evt_api_is_ready = false; + +bool events_api_is_ready(void) +{ + return evt_api_is_ready; +} + +void *events_api_thread(void *thread_context) +{ + events_api_thread_config *c = + (events_api_thread_config *)thread_context; + struct sched_param sched_param; + int policy = 0; + int res; + + nfds_t i; + struct sockaddr_un addr; + int server_socket; + int conn_socket; + size_t len; + + LOG("starting\n"); + + res = pthread_getschedparam(pthread_self(), &policy, &sched_param); + if (res) { + LOG("error getting scheduler params: %s\n", strerror(res)); + } else { + policy = c->sched_policy; + sched_param.sched_priority = c->sched_priority; + + res = pthread_setschedparam(pthread_self(), + policy, + &sched_param); + if (res) { + LOG("error setting scheduler params" + " (got root?): %s\n", strerror(res)); + } + } + + unlink(c->global->api_socket); + + server_socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_socket < 0) { + LOG("failed to create server socket.\n"); + goto out_exit; + } + LOG("created server socket.\n"); + + addr.sun_family = AF_UNIX; + + len = strnlen(c->global->api_socket, sizeof(addr.sun_path) - 1); + memcpy(addr.sun_path, c->global->api_socket, len); + addr.sun_path[len] = '\0'; + + if (bind(server_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + LOG("failed to bind server socket.\n"); + goto out_close_server; + } + LOG("server socket bind success.\n"); + + if (listen(server_socket, 20) < 0) { + LOG("failed to listen on socket.\n"); + goto out_close_server; + } + LOG("listening for connections.\n"); + + evt_api_is_ready = true; + + pollfds[SRV_SOCKET].fd = server_socket; + pollfds[SRV_SOCKET].events = POLLIN | POLLPRI; + num_fds = 1; + + while (c->global->running) { + + res = poll(pollfds, num_fds, 100); + if (res < 0) { + LOG("poll error\n"); + continue; + } + + if (0 == res) // timeout + continue; + + if ((nfds_t)res > num_fds) { // weird + LOG("something bad happened during poll!\n"); + continue; + } + + // handle requests on existing sockets + for (i = FIRST_CLIENT_SOCKET ; i < num_fds ; ++i) { + if (pollfds[i].revents) { + handle_message(pollfds[i].fd); + } + } + + // handle new connection requests + if (pollfds[SRV_SOCKET].revents) { + + if (num_fds == MAX_CLIENT_CONNECTIONS+1) { + LOG("exceeded max connections!\n"); + continue; + } + + conn_socket = accept(server_socket, NULL, NULL); + + if (conn_socket < 0) { + LOG("failed to accept new connection!\n"); + } else { + LOG("accepting connection %d.\n", conn_socket); + + pollfds[num_fds].fd = conn_socket; + pollfds[num_fds].events = POLLIN | POLLPRI; + ++num_fds; + } + + } + + } + + opae_api_unregister_all_events(); + + // close any active client sockets + for (i = FIRST_CLIENT_SOCKET ; i < num_fds ; ++i) { + close(pollfds[i].fd); + } + +out_close_server: + evt_api_is_ready = false; + close(server_socket); +out_exit: + LOG("exiting\n"); + return NULL; +} diff --git a/tools/fpgad/events_api_thread.h b/tools/fpgad/events_api_thread.h new file mode 100644 index 0000000..c097a4d --- /dev/null +++ b/tools/fpgad/events_api_thread.h @@ -0,0 +1,43 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef __FPGAD_EVENTS_API_THREAD_H__ +#define __FPGAD_EVENTS_API_THREAD_H__ + +#include "fpgad.h" +#include + +typedef struct _events_api_thread_config { + struct fpgad_config *global; + int sched_policy; + int sched_priority; +} events_api_thread_config; + +extern events_api_thread_config events_api_config; + +void *events_api_thread(void *); + +#endif /* __FPGAD_EVENTS_API_THREAD_H__ */ diff --git a/tools/fpgad/fpgad.c b/tools/fpgad/fpgad.c new file mode 100644 index 0000000..ff88525 --- /dev/null +++ b/tools/fpgad/fpgad.c @@ -0,0 +1,192 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include "fpgad.h" +#include "monitor_thread.h" +#include "event_dispatcher_thread.h" +#include "events_api_thread.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("main: " format, ##__VA_ARGS__) + +struct fpgad_config global_config; + +void sig_handler(int sig, siginfo_t *info, void *unused) +{ + UNUSED_PARAM(info); + UNUSED_PARAM(unused); + switch (sig) { + case SIGINT: + // Process interrupted. + LOG("Got SIGINT. Exiting.\n"); + global_config.running = false; + break; + case SIGTERM: + // Process terminated. + LOG("Got SIGTERM. Exiting.\n"); + global_config.running = false; + break; + } +} + +int main(int argc, char *argv[]) +{ + int res; + FILE *fp; + + memset(&global_config, 0, sizeof(global_config)); + + global_config.poll_interval_usec = 100 * 1000; + global_config.running = true; + global_config.api_socket = "/tmp/fpga_event_socket"; + global_config.num_null_gbs = 0; + + log_set(stdout); + + res = cmd_parse_args(&global_config, argc, argv); + if (res != 0) { + if (res == -2) + res = 0; + else + LOG("error parsing command line.\n"); + goto out_destroy; + } + + if (cmd_canonicalize_paths(&global_config)) { + LOG("error with paths.\n"); + goto out_destroy; + } + + if (global_config.daemon) { + + res = daemonize(sig_handler, + global_config.filemode, + global_config.directory); + if (res != 0) { + LOG("daemonize failed: %s\n", strerror(res)); + goto out_destroy; + } + + + } else { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; + sa.sa_sigaction = sig_handler; + + res = sigaction(SIGINT, &sa, NULL); + if (res < 0) { + LOG("failed to register SIGINT handler.\n"); + goto out_destroy; + } + + res = sigaction(SIGTERM, &sa, NULL); + if (res < 0) { + LOG("failed to register SIGTERM handler.\n"); + goto out_destroy; + } + } + + if (log_open(global_config.logfile) < 0) { + LOG("failed to open log file\n"); + res = 1; + goto out_destroy; + } + + fp = fopen(global_config.pidfile, "w"); + if (NULL == fp) { + LOG("failed to open pid file\n"); + res = 1; + goto out_destroy; + } + fprintf(fp, "%d\n", getpid()); + fclose(fp); + + res = mon_enumerate(&global_config); + if (res) { + LOG("OPAE device enumeration failed\n"); + goto out_destroy; + } + + res = pthread_create(&global_config.event_dispatcher_thr, + NULL, + event_dispatcher_thread, + &event_dispatcher_config); + if (res) { + LOG("failed to create event_dispatcher_thread\n"); + global_config.running = false; + goto out_destroy; + } + + while (!evt_dispatcher_is_ready()) + usleep(1); + + res = pthread_create(&global_config.monitor_thr, + NULL, + monitor_thread, + &monitor_config); + if (res) { + LOG("failed to create monitor_thread\n"); + global_config.running = false; + goto out_stop_event_dispatcher; + } + + res = pthread_create(&global_config.events_api_thr, + NULL, + events_api_thread, + &events_api_config); + if (res) { + LOG("failed to create events_api_thread\n"); + global_config.running = false; + goto out_stop_monitor; + } + + if (pthread_join(global_config.events_api_thr, NULL)) { + LOG("failed to join events_api_thread\n"); + } +out_stop_monitor: + if (pthread_join(global_config.monitor_thr, NULL)) { + LOG("failed to join monitor_thread\n"); + } +out_stop_event_dispatcher: + if (pthread_join(global_config.event_dispatcher_thr, NULL)) { + LOG("failed to join event_dispatcher_thread\n"); + } +out_destroy: + mon_destroy(&global_config); + cmd_destroy(&global_config); + log_close(); + return res; +} diff --git a/tools/fpgad/fpgad.cfg b/tools/fpgad/fpgad.cfg new file mode 100644 index 0000000..41a656e --- /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 0000000..7fad49d --- /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 0000000..ed50109 --- /dev/null +++ b/tools/fpgad/fpgad.h @@ -0,0 +1,76 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef __FPGAD_FPGAD_H__ +#define __FPGAD_FPGAD_H__ + +#ifndef __USE_GNU +#define __USE_GNU +#endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "opae_int.h" + +#include "api/logging.h" +#include "command_line.h" + +int daemonize(void (*hndlr)(int, siginfo_t *, void *), + mode_t mask, + const char *dir); + + +#define fpgad_mutex_lock(__res, __mtx_ptr) \ + ({ \ + (__res) = pthread_mutex_lock(__mtx_ptr); \ + if (__res) \ + LOG("pthread_mutex_lock failed: %s", \ + strerror(errno)); \ + __res; \ + }) + +#define fpgad_mutex_unlock(__res, __mtx_ptr) \ + ({ \ + (__res) = pthread_mutex_unlock(__mtx_ptr); \ + if (__res) \ + LOG("pthread_mutex_unlock failed: %s", \ + strerror(errno)); \ + __res; \ + }) + +#endif /* __FPGAD_FPGAD_H__ */ diff --git a/tools/fpgad/fpgad.service.in b/tools/fpgad/fpgad.service.in new file mode 100644 index 0000000..0ec5bac --- /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 0000000..d4cfd9c --- /dev/null +++ b/tools/fpgad/monitor_thread.c @@ -0,0 +1,261 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include "monitored_device.h" +#include "monitor_thread.h" +#include "event_dispatcher_thread.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("monitor_thread: " format, ##__VA_ARGS__) + +monitor_thread_config monitor_config = { + .global = &global_config, + .sched_policy = SCHED_RR, + .sched_priority = 20, +}; + +STATIC pthread_mutex_t mon_list_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +STATIC fpgad_monitored_device *monitored_device_list; + +STATIC void mon_queue_response(fpgad_detection_status status, + fpgad_respond_event_t response, + fpgad_monitored_device *d, + void *response_context) +{ + if (status == FPGAD_STATUS_DETECTED_HIGH) { + + if (evt_queue_response_high(response, + d, + response_context)) { + pthread_yield(); + } else { + LOG("high priority event queue is full. Dropping!\n"); + } + + } else if (status == FPGAD_STATUS_DETECTED) { + + if (evt_queue_response(response, + d, + response_context)) { + pthread_yield(); + } else { + LOG("event queue is full. Dropping!\n"); + } + + } +} + +STATIC void mon_monitor(fpgad_monitored_device *d) +{ + unsigned i; + + if (!d->detections) + return; + + for (i = 0 ; d->detections[i] ; ++i) { + fpgad_detection_status result; + fpgad_detect_event_t detect = + d->detections[i]; + void *detect_context = + d->detection_contexts ? + d->detection_contexts[i] : NULL; + + result = detect(d, detect_context); + + if (result != FPGAD_STATUS_NOT_DETECTED && d->responses) { + fpgad_respond_event_t response = + d->responses[i]; + void *response_context = + d->response_contexts ? + d->response_contexts[i] : NULL; + + if (response) { + mon_queue_response(result, + response, + d, + response_context); + } + } + } +} + +STATIC volatile bool mon_is_ready = false; + +bool monitor_is_ready(void) +{ + return mon_is_ready; +} + +void *monitor_thread(void *thread_context) +{ + monitor_thread_config *c = (monitor_thread_config *)thread_context; + struct sched_param sched_param; + int policy = 0; + int res; + int err; + fpgad_monitored_device *d; + + LOG("starting\n"); + + res = pthread_getschedparam(pthread_self(), &policy, &sched_param); + if (res) { + LOG("error getting scheduler params: %s\n", strerror(res)); + } else { + policy = c->sched_policy; + sched_param.sched_priority = c->sched_priority; + + res = pthread_setschedparam(pthread_self(), + policy, + &sched_param); + if (res) { + LOG("error setting scheduler params" + " (got root?): %s\n", strerror(res)); + } + } + + mon_is_ready = true; + + while (c->global->running) { + fpgad_mutex_lock(err, &mon_list_lock); + + for (d = monitored_device_list ; d ; d = d->next) { + mon_monitor(d); + } + + fpgad_mutex_unlock(err, &mon_list_lock); + + usleep(c->global->poll_interval_usec); + } + + while (evt_dispatcher_is_ready()) { + // Wait for the event dispatcher to complete + // before we destroy the monitored devices. + usleep(c->global->poll_interval_usec); + } + + mon_destroy(c->global); + mon_is_ready = false; + + LOG("exiting\n"); + return NULL; +} + +void mon_monitor_device(fpgad_monitored_device *d) +{ + int err; + fpgad_monitored_device *trav; + + fpgad_mutex_lock(err, &mon_list_lock); + + d->next = NULL; + + if (!monitored_device_list) { + monitored_device_list = d; + goto out_unlock; + } + + for (trav = monitored_device_list ; + trav->next ; + trav = trav->next) + /* find the end of the list */ ; + + trav->next = d; + +out_unlock: + fpgad_mutex_unlock(err, &mon_list_lock); +} + +void mon_destroy(struct fpgad_config *c) +{ + unsigned i; + int err; + fpgad_monitored_device *d; + + fpgad_mutex_lock(err, &mon_list_lock); + + for (d = monitored_device_list ; d ; ) { + fpgad_monitored_device *trash = d; + fpgad_plugin_destroy_t destroy; + + d = d->next; + + if (trash->type == FPGAD_PLUGIN_TYPE_THREAD) { + + if (trash->thread_stop_fn) { + trash->thread_stop_fn(); + } else { + LOG("Thread plugin \"%s\" has" + " no thread_stop_fn\n", + trash->supported->library_path); + pthread_cancel(trash->thread); + } + + pthread_join(trash->thread, NULL); + } + + destroy = (fpgad_plugin_destroy_t) + dlsym(trash->supported->dl_handle, + FPGAD_PLUGIN_DESTROY); + + if (destroy) { + destroy(trash); + } else { + LOG("warning - no destructor for \"%s\"\n", + trash->supported->library_path); + } + + if (trash->token) + fpgaDestroyToken(&trash->token); + + free(trash); + } + monitored_device_list = NULL; + + if (c->supported_devices) { + + for (i = 0 ; c->supported_devices[i].library_path ; ++i) { + fpgad_supported_device *d = &c->supported_devices[i]; + + if (d->flags & FPGAD_DEV_LOADED) { + dlclose(d->dl_handle); + } + + d->flags = 0; + d->dl_handle = NULL; + } + + } + + fpgad_mutex_unlock(err, &mon_list_lock); +} diff --git a/tools/fpgad/monitor_thread.h b/tools/fpgad/monitor_thread.h new file mode 100644 index 0000000..407e179 --- /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 0000000..9860c8a --- /dev/null +++ b/tools/fpgad/monitored_device.c @@ -0,0 +1,394 @@ +// Copyright(c) 2018-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include + +#include + +#include "monitored_device.h" +#include "monitor_thread.h" +#include "api/sysfs.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("monitored_device: " format, ##__VA_ARGS__) + +fpgad_supported_device default_supported_devices_table[] = { + { 0x8086, 0xbcc0, "libfpgad-xfpga.so", 0, NULL, "" }, + { 0x8086, 0xbcc1, "libfpgad-xfpga.so", 0, NULL, "" }, + { 0x8086, 0x0b30, "libfpgad-vc.so", 0, NULL, "" }, + { 0x8086, 0x0b31, "libfpgad-vc.so", 0, NULL, "" }, + { 0, 0, NULL, 0, NULL, "" }, +}; + +STATIC fpgad_supported_device *mon_is_loaded(struct fpgad_config *c, + const char *library_path) +{ + unsigned i; + int res = 0; + + for (i = 0 ; c->supported_devices[i].library_path ; ++i) { + fpgad_supported_device *d = &c->supported_devices[i]; + + res = strcmp(library_path, d->library_path); + + if (!res && (d->flags & FPGAD_DEV_LOADED)) + return d; + } + return NULL; +} + +STATIC fpgad_monitored_device * +allocate_monitored_device(struct fpgad_config *config, + fpgad_supported_device *supported, + fpga_token token, + uint64_t object_id, + fpga_objtype object_type, + opae_bitstream_info *bitstr) +{ + fpgad_monitored_device *d; + + d = (fpgad_monitored_device *) calloc( + 1, sizeof(fpgad_monitored_device)); + + if (!d) { + LOG("out of memory"); + return NULL; + } + + d->config = config; + d->supported = supported; + d->token = token; + d->object_id = object_id; + d->object_type = object_type; + d->bitstr = bitstr; + + return d; +} + +STATIC void *mon_find_plugin(const char *libpath) +{ + char plugin_path[PATH_MAX]; + const char *search_paths[] = { OPAE_MODULE_SEARCH_PATHS }; + unsigned i; + void *dl_handle; + + for (i = 0 ; + i < sizeof(search_paths) / sizeof(search_paths[0]) ; + ++i) { + snprintf(plugin_path, sizeof(plugin_path), + "%s%s", search_paths[i], libpath); + + dl_handle = dlopen(plugin_path, RTLD_LAZY|RTLD_LOCAL); + if (dl_handle) + return dl_handle; + } + + return NULL; +} + +STATIC bool mon_consider_device(struct fpgad_config *c, fpga_token token) +{ + unsigned i; + fpga_properties props = NULL; + fpga_token parent = NULL; + fpga_properties parent_props = NULL; + fpga_result res; + uint16_t vendor_id; + uint16_t device_id; + uint64_t object_id; + fpga_objtype object_type; + opae_bitstream_info *bitstr = NULL; + fpga_guid pr_ifc_id; + bool added = false; + + res = fpgaGetProperties(token, &props); + if (res != FPGA_OK) { + LOG("failed to get properties\n"); + return false; + } + + vendor_id = 0; + res = fpgaPropertiesGetVendorID(props, &vendor_id); + if (res != FPGA_OK) { + LOG("failed to get vendor ID\n"); + goto err_out_destroy; + } + + device_id = 0; + res = fpgaPropertiesGetDeviceID(props, &device_id); + if (res != FPGA_OK) { + LOG("failed to get device ID\n"); + goto err_out_destroy; + } + + object_id = 0; + res = fpgaPropertiesGetObjectID(props, &object_id); + if (res != FPGA_OK) { + LOG("failed to get object ID\n"); + goto err_out_destroy; + } + + object_type = FPGA_ACCELERATOR; + res = fpgaPropertiesGetObjectType(props, &object_type); + if (res != FPGA_OK) { + LOG("failed to get object type\n"); + goto err_out_destroy; + } + + // Do we have a NULL GBS from the command line + // that matches this device? + + if (object_type == FPGA_DEVICE) { + // The token's guid is the PR interface ID. + + res = fpgaPropertiesGetGUID(props, &pr_ifc_id); + if (res != FPGA_OK) { + LOG("failed to get PR interface ID\n");\ + goto err_out_destroy; + } + + for (i = 0 ; i < c->num_null_gbs ; ++i) { + if (!uuid_compare(c->null_gbs[i].pr_interface_id, + pr_ifc_id)) { + bitstr = &c->null_gbs[i]; + break; + } + } + } else { + // The parent token's guid is the PR interface ID. + + res = fpgaPropertiesGetParent(props, &parent); + if (res != FPGA_OK) { + LOG("failed to get parent token\n"); + goto err_out_destroy; + } + + res = fpgaGetProperties(parent, &parent_props); + if (res != FPGA_OK) { + LOG("failed to get parent properties\n"); + goto err_out_destroy; + } + + res = fpgaPropertiesGetGUID(parent_props, &pr_ifc_id); + if (res != FPGA_OK) { + LOG("failed to get PR interface ID\n"); + goto err_out_destroy; + } + + fpgaDestroyProperties(&parent_props); + fpgaDestroyToken(&parent); + + for (i = 0 ; i < c->num_null_gbs ; ++i) { + if (!uuid_compare(c->null_gbs[i].pr_interface_id, + pr_ifc_id)) { + bitstr = &c->null_gbs[i]; + break; + } + } + } + + fpgaDestroyProperties(&props); + + for (i = 0 ; c->supported_devices[i].library_path ; ++i) { + fpgad_supported_device *d = &c->supported_devices[i]; + + // Do we support this device? + if (d->vendor_id == vendor_id && + d->device_id == device_id) { + fpgad_supported_device *loaded_by; + fpgad_monitored_device *monitored; + fpgad_plugin_configure_t cfg; + int res; + + d->flags |= FPGAD_DEV_DETECTED; + + // Is the fpgad plugin already loaded? + loaded_by = mon_is_loaded(c, d->library_path); + + if (loaded_by) { + // The two table entries will share the + // same plugin handle (but only loaded_by + // will have FPGAD_DEV_LOADED). + d->dl_handle = loaded_by->dl_handle; + } else { + // Plugin hasn't been loaded. + // Load it now. + d->dl_handle = + mon_find_plugin(d->library_path); + if (!d->dl_handle) { + char *err = dlerror(); + LOG("failed to load \"%s\" %s\n", + d->library_path, + err ? err : ""); + continue; + } + + d->flags |= FPGAD_DEV_LOADED; + } + + if (!bitstr) { + LOG("Warning: no NULL GBS for vid=0x%04x " + "did=0x%04x objid=0x%x (%s)\n", + vendor_id, + device_id, + object_id, + object_type == FPGA_ACCELERATOR ? + "accelerator" : "device"); + } + + // Add the device to the monitored list. + monitored = allocate_monitored_device(c, + d, + token, + object_id, + object_type, + bitstr); + if (!monitored) { + LOG("failed to add device 0x%04x:0x%04x\n", + vendor_id, device_id); + continue; + } + + // Success + cfg = (fpgad_plugin_configure_t) + dlsym(d->dl_handle, + FPGAD_PLUGIN_CONFIGURE); + if (!cfg) { + LOG("failed to find %s in \"%s\"\n", + FPGAD_PLUGIN_CONFIGURE, + d->library_path); + free(monitored); + continue; + } + + res = cfg(monitored, d->config); + if (res) { + LOG("%s for \"%s\" returned %d.\n", + FPGAD_PLUGIN_CONFIGURE, + d->library_path, + res); + free(monitored); + continue; + } + + if (monitored->type == FPGAD_PLUGIN_TYPE_THREAD) { + + if (monitored->thread_fn) { + + if (pthread_create(&monitored->thread, + NULL, + monitored->thread_fn, + monitored)) { + LOG("failed to create thread" + " for \"%s\"\n", + d->library_path); + free(monitored); + continue; + } + + } else { + LOG("Thread plugin \"%s\" has no " + "thread_fn\n", d->library_path); + free(monitored); + continue; + } + + } + + mon_monitor_device(monitored); + added = true; + break; + } + } + + return added; + +err_out_destroy: + if (props) + fpgaDestroyProperties(&props); + if (parent) + fpgaDestroyToken(&parent); + if (parent_props) + fpgaDestroyProperties(&parent_props); + return false; +} + +int mon_enumerate(struct fpgad_config *c) +{ + fpga_token *tokens = NULL; + fpga_result res; + uint32_t num_matches = 0; + uint32_t i; + unsigned monitored_devices = 0; + + res = fpgaEnumerate(NULL, 0, NULL, 0, &num_matches); + if (res != FPGA_OK) { + LOG("enumeration failed\n"); + return res; + } + + if (!num_matches) { + res = 1; + return res; + } + + tokens = calloc(num_matches, sizeof(fpga_token)); + if (!tokens) { + res = 1; + LOG("out of memory\n"); + return res; + } + + res = fpgaEnumerate(NULL, 0, tokens, num_matches, &num_matches); + if (res != FPGA_OK) { + LOG("enumeration failed (2)\n"); + goto out_exit; + } + + for (i = 0 ; i < num_matches ; ++i) { + if (!mon_consider_device(c, tokens[i])) { + // Not monitoring it. Destroy the token. + fpgaDestroyToken(&tokens[i]); + } else { + ++monitored_devices; + } + } + +out_exit: + if (tokens) + free(tokens); + return res + (monitored_devices ? 0 : 1); +} diff --git a/tools/fpgad/monitored_device.h b/tools/fpgad/monitored_device.h new file mode 100644 index 0000000..c2ef50e --- /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 0000000..fd43b5b --- /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 0000000..c05b626 --- /dev/null +++ b/tools/fpgad/plugins/fpgad-vc/fpgad-vc.c @@ -0,0 +1,1085 @@ +// Copyright(c) 2019-2020, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include + +#include "fpgad/api/opae_events_api.h" +#include "fpgad/api/device_monitoring.h" +#include "fpgad/api/sysfs.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("fpgad-vc: " format, ##__VA_ARGS__) + +#define SYSFS_PATH_MAX 256 + +#define TRIM_LOG_MODULUS 20 +#define LOG_MOD(__r, __fmt, ...) \ +do { \ +\ + ++(__r); \ + if (!((__r) % TRIM_LOG_MODULUS)) { \ + log_printf("fpgad-vc: " __fmt, ##__VA_ARGS__); \ + } \ +} while (0) + + +typedef struct _vc_sensor { + fpga_object sensor_object; + char *name; + char *type; + uint64_t id; + fpga_object value_object; + uint64_t value; + uint64_t high_fatal; + uint64_t high_warn; + uint64_t low_fatal; + uint64_t low_warn; + uint32_t flags; +#define FPGAD_SENSOR_VC_IGNORE 0x00000001 +#define FPGAD_SENSOR_VC_HIGH_FATAL_VALID 0x00000002 +#define FPGAD_SENSOR_VC_HIGH_WARN_VALID 0x00000004 +#define FPGAD_SENSOR_VC_LOW_FATAL_VALID 0x00000008 +#define FPGAD_SENSOR_VC_LOW_WARN_VALID 0x00000010 + uint32_t read_errors; +#define FPGAD_SENSOR_VC_MAX_READ_ERRORS 25 +} vc_sensor; + +typedef struct _vc_config_sensor { + uint64_t id; + uint64_t high_fatal; + uint64_t high_warn; + uint64_t low_fatal; + uint64_t low_warn; + uint32_t flags; +} vc_config_sensor; + +#define MAX_VC_SENSORS 128 +typedef struct _vc_device { + fpgad_monitored_device *base_device; + fpga_object group_object; + vc_sensor sensors[MAX_VC_SENSORS]; + uint32_t num_sensors; + uint64_t max_sensor_id; + uint8_t *state_tripped; // bit set + uint8_t *state_last; // bit set + uint64_t tripped_count; + uint32_t num_config_sensors; + vc_config_sensor *config_sensors; + bool aer_disabled; + uint32_t previous_ecap_aer[2]; +} vc_device; + +#define BIT_SET_MASK(__n) (1 << ((__n) % 8)) +#define BIT_SET_INDEX(__n) ((__n) / 8) + +#define BIT_SET_SET(__s, __n) \ +((__s)[BIT_SET_INDEX(__n)] |= BIT_SET_MASK(__n)) + +#define BIT_SET_CLR(__s, __n) \ +((__s)[BIT_SET_INDEX(__n)] &= ~BIT_SET_MASK(__n)) + +#define BIT_IS_SET(__s, __n) \ +((__s)[BIT_SET_INDEX(__n)] & BIT_SET_MASK(__n)) + + +#define HIGH_FATAL(__sens) \ +(((__sens)->flags & FPGAD_SENSOR_VC_HIGH_FATAL_VALID) && \ + ((__sens)->value > (__sens)->high_fatal)) + +#define HIGH_WARN(__sens) \ +(((__sens)->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) && \ + ((__sens)->value > (__sens)->high_warn)) + +#define HIGH_NORMAL(__sens) \ +(((__sens)->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) && \ + ((__sens)->value <= (__sens)->high_warn)) + +#define LOW_FATAL(__sens) \ +(((__sens)->flags & FPGAD_SENSOR_VC_LOW_FATAL_VALID) && \ + ((__sens)->value < (__sens)->low_fatal)) + +#define LOW_WARN(__sens) \ +(((__sens)->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) && \ + ((__sens)->value < (__sens)->low_warn)) + +#define LOW_NORMAL(__sens) \ +(((__sens)->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) && \ + ((__sens)->value >= (__sens)->low_warn)) + +STATIC bool vc_threads_running = true; + +STATIC void stop_vc_threads(void) +{ + vc_threads_running = false; +} + +STATIC void vc_destroy_sensor(vc_sensor *sensor) +{ + fpgaDestroyObject(&sensor->sensor_object); + if (sensor->name) { + free(sensor->name); + sensor->name = NULL; + } + if (sensor->type) { + free(sensor->type); + sensor->type = NULL; + } + if (sensor->value_object) { + fpgaDestroyObject(&sensor->value_object); + sensor->value_object = NULL; + } + sensor->flags = 0; + sensor->read_errors = 0; +} + +STATIC void vc_destroy_sensors(vc_device *vc) +{ + uint32_t i; + + for (i = 0 ; i < vc->num_sensors ; ++i) { + vc_destroy_sensor(&vc->sensors[i]); + } + vc->num_sensors = 0; + vc->max_sensor_id = 0; + + if (vc->group_object) { + fpgaDestroyObject(&vc->group_object); + vc->group_object = NULL; + } + + if (vc->state_tripped) { + free(vc->state_tripped); + vc->state_tripped = NULL; + } + + if (vc->state_last) { + free(vc->state_last); + vc->state_last = NULL; + } +} + +STATIC void vc_destroy_device(vc_device *vc) +{ + vc_destroy_sensors(vc); + if (vc->config_sensors) { + free(vc->config_sensors); + vc->config_sensors = NULL; + vc->num_config_sensors = 0; + } +} + +STATIC fpga_result vc_sensor_get_string(vc_sensor *sensor, + const char *obj_name, + char **name) +{ + fpga_result res; + fpga_object obj = NULL; + char buf[SYSFS_PATH_MAX] = { 0, }; + uint32_t len = 0; + + res = fpgaObjectGetObject(sensor->sensor_object, + obj_name, + &obj, + 0); + if (res != FPGA_OK) + return res; + + res = fpgaObjectGetSize(obj, &len, 0); + if (res != FPGA_OK) + goto out_free_obj; + + res = fpgaObjectRead(obj, (uint8_t *)buf, 0, len, 0); + if (res != FPGA_OK) + goto out_free_obj; + + if (buf[len-1] == '\n') + buf[len-1] = '\0'; + + *name = cstr_dup((const char *)buf); + if (!(*name)) + res = FPGA_NO_MEMORY; + +out_free_obj: + fpgaDestroyObject(&obj); + return res; +} + +STATIC fpga_result vc_sensor_get_u64(vc_sensor *sensor, + const char *obj_name, + uint64_t *value) +{ + fpga_result res; + fpga_object obj = NULL; + + res = fpgaObjectGetObject(sensor->sensor_object, + obj_name, + &obj, + 0); + if (res != FPGA_OK) + return res; + + res = fpgaObjectRead64(obj, value, 0); + + fpgaDestroyObject(&obj); + + return res; +} + +// The percentage by which we adjust the power trip +// points so that we catch anomolies before the hw does. +#define VC_PERCENT_ADJUST_PWR 5 +// The number of degrees by which we adjust the +// temperature trip points so that we catch anomolies +// before the hw does. +#define VC_DEGREES_ADJUST_TEMP 5 +STATIC fpga_result vc_sensor_get(vc_device *vc, vc_sensor *s) +{ + fpga_result res; + bool is_temperature; + int indicator = -1; + vc_config_sensor *cfg_sensor = NULL; + uint32_t i; + + if (s->name) { + free(s->name); + s->name = NULL; + } + + if (s->type) { + free(s->type); + s->type = NULL; + } + + res = vc_sensor_get_string(s, "name", &s->name); + if (res != FPGA_OK) + return res; + + res = vc_sensor_get_string(s, "type", &s->type); + if (res != FPGA_OK) + return res; + + res = vc_sensor_get_u64(s, "id", &s->id); + if (res != FPGA_OK) + return res; + + res = fpgaObjectRead64(s->value_object, &s->value, 0); + if (res != FPGA_OK) + return res; + + indicator = strcmp(s->type, "Temperature"); + is_temperature = (indicator == 0); + + res = vc_sensor_get_u64(s, "high_fatal", &s->high_fatal); + if (res == FPGA_OK) { + s->flags |= FPGAD_SENSOR_VC_HIGH_FATAL_VALID; + if (is_temperature) + s->high_fatal -= VC_DEGREES_ADJUST_TEMP; + else + s->high_fatal -= + (s->high_fatal * VC_PERCENT_ADJUST_PWR) / 100; + } else + s->flags &= ~FPGAD_SENSOR_VC_HIGH_FATAL_VALID; + + res = vc_sensor_get_u64(s, "high_warn", &s->high_warn); + if (res == FPGA_OK) + s->flags |= FPGAD_SENSOR_VC_HIGH_WARN_VALID; + else + s->flags &= ~FPGAD_SENSOR_VC_HIGH_WARN_VALID; + + res = vc_sensor_get_u64(s, "low_fatal", &s->low_fatal); + if (res == FPGA_OK) { + s->flags |= FPGAD_SENSOR_VC_LOW_FATAL_VALID; + if (is_temperature) + s->low_fatal += VC_DEGREES_ADJUST_TEMP; + else + s->low_fatal += + (s->low_fatal * VC_PERCENT_ADJUST_PWR) / 100; + } else + s->flags &= ~FPGAD_SENSOR_VC_LOW_FATAL_VALID; + + res = vc_sensor_get_u64(s, "low_warn", &s->low_warn); + if (res == FPGA_OK) + s->flags |= FPGAD_SENSOR_VC_LOW_WARN_VALID; + else + s->flags &= ~FPGAD_SENSOR_VC_LOW_WARN_VALID; + + /* Do we have a user override (via the config) for + * this sensor? If so, then honor it. + */ + for (i = 0 ; i < vc->num_config_sensors ; ++i) { + if (vc->config_sensors[i].flags & + FPGAD_SENSOR_VC_IGNORE) + continue; + if (vc->config_sensors[i].id == s->id) { + cfg_sensor = &vc->config_sensors[i]; + break; + } + } + + if (cfg_sensor) { + + if (cfg_sensor->flags & FPGAD_SENSOR_VC_HIGH_FATAL_VALID) { + // Cap the sensor at the adjusted max + // allowed by the hardware. + if ((s->flags & FPGAD_SENSOR_VC_HIGH_FATAL_VALID) && + (cfg_sensor->high_fatal > s->high_fatal)) + /* nothing */ ; + else + s->high_fatal = cfg_sensor->high_fatal; + + s->flags |= FPGAD_SENSOR_VC_HIGH_FATAL_VALID; + } else + s->flags &= ~FPGAD_SENSOR_VC_HIGH_FATAL_VALID; + + if (cfg_sensor->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) { + + if ((s->flags & FPGAD_SENSOR_VC_HIGH_WARN_VALID) && + (cfg_sensor->high_warn > s->high_warn)) + /* nothing */ ; + else + s->high_warn = cfg_sensor->high_warn; + + s->flags |= FPGAD_SENSOR_VC_HIGH_WARN_VALID; + } else + s->flags &= ~FPGAD_SENSOR_VC_HIGH_WARN_VALID; + + if (cfg_sensor->flags & FPGAD_SENSOR_VC_LOW_FATAL_VALID) { + + if ((s->flags & FPGAD_SENSOR_VC_LOW_FATAL_VALID) && + (cfg_sensor->low_fatal < s->low_fatal)) + /* nothing */ ; + else + s->low_fatal = cfg_sensor->low_fatal; + + s->flags |= FPGAD_SENSOR_VC_LOW_FATAL_VALID; + } else + s->flags &= ~FPGAD_SENSOR_VC_LOW_FATAL_VALID; + + if (cfg_sensor->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) { + + if ((s->flags & FPGAD_SENSOR_VC_LOW_WARN_VALID) && + (cfg_sensor->low_warn < s->low_warn)) + /* nothing */ ; + else + s->low_warn = cfg_sensor->low_warn; + + s->flags |= FPGAD_SENSOR_VC_LOW_WARN_VALID; + } else + s->flags &= ~FPGAD_SENSOR_VC_LOW_WARN_VALID; + + } + + return FPGA_OK; +} + +STATIC fpga_result vc_enum_sensor(vc_device *vc, + const char *name) +{ + fpga_result res; + vc_sensor *s; + + if (vc->num_sensors == MAX_VC_SENSORS) { + LOG("exceeded max number of sensors.\n"); + return FPGA_EXCEPTION; + } + + s = &vc->sensors[vc->num_sensors]; + + res = fpgaObjectGetObject(vc->group_object, + name, + &s->sensor_object, + 0); + + if (res != FPGA_OK) + return res; + + res = fpgaObjectGetObject(s->sensor_object, + "value", + &s->value_object, + 0); + + if (res != FPGA_OK) { + LOG("failed to get value object for %s.\n", name); + fpgaDestroyObject(&s->sensor_object); + return res; + } + + res = vc_sensor_get(vc, s); + if (res == FPGA_OK) + ++vc->num_sensors; + else { + LOG("warning: sensor attribute enumeration failed.\n"); + vc_destroy_sensor(s); + } + + return res; +} + +STATIC fpga_result vc_enum_sensors(vc_device *vc) +{ + fpga_result res; + char name[SYSFS_PATH_MAX]; + int i; + + res = fpgaTokenGetObject(vc->base_device->token, + "spi-altera.*.auto/spi_master/spi*/spi*.*", + &vc->group_object, + FPGA_OBJECT_GLOB); + if (res) + return res; + + for (i = 0 ; i < MAX_VC_SENSORS ; ++i) { + snprintf(name, sizeof(name), "sensor%d", i); + vc_enum_sensor(vc, name); + } + + if (vc->num_sensors > 0) { + vc_sensor *s = &vc->sensors[vc->num_sensors - 1]; + + vc->max_sensor_id = s->id; + + vc->state_tripped = calloc((vc->max_sensor_id + 7) / 8, 1); + vc->state_last = calloc((vc->max_sensor_id + 7) / 8, 1); + + return (vc->state_tripped && vc->state_last) ? + FPGA_OK : FPGA_NO_MEMORY; + } + + return FPGA_NOT_FOUND; +} + +STATIC fpga_result vc_disable_aer(vc_device *vc) +{ + fpga_token token; + fpga_result res; + fpga_properties prop = NULL; + char path[PATH_MAX]; + char rlpath[PATH_MAX]; + char *p; + char cmd[256]; + char output[256]; + FILE *fp; + size_t sz; + + uint16_t seg = 0; + uint8_t bus = 0; + uint8_t dev = 0; + uint8_t fn = 0; + + token = vc->base_device->token; + + res = fpgaGetProperties(token, &prop); + if (res != FPGA_OK) { + LOG("failed to get fpga properties.\n"); + return res; + } + + if ((fpgaPropertiesGetSegment(prop, &seg) != FPGA_OK) || + (fpgaPropertiesGetBus(prop, &bus) != FPGA_OK) || + (fpgaPropertiesGetDevice(prop, &dev) != FPGA_OK) || + (fpgaPropertiesGetFunction(prop, &fn) != FPGA_OK)) { + LOG("failed to get PCI attributes.\n"); + fpgaDestroyProperties(&prop); + return FPGA_EXCEPTION; + } + + fpgaDestroyProperties(&prop); + + snprintf(path, sizeof(path), + "/sys/bus/pci/devices/%04x:%02x:%02x.%d", + (int)seg, (int)bus, (int)dev, (int)fn); + + memset(rlpath, 0, sizeof(rlpath)); + + if (readlink(path, rlpath, sizeof(rlpath)) < 0) { + LOG("readlink \"%s\" failed.\n", path); + return FPGA_EXCEPTION; + } + + // (rlpath) + // 1111111111 + // 01234567890123456789 + // ../../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.0/ + // 0000:b0:09.0/0000:b2:00.0 + + p = strstr(rlpath, "devices/pci"); + + p += 19; + *(p + 12) = '\0'; + + // Save the current ECAP_AER values. + + snprintf(cmd, sizeof(cmd), + "setpci -s %s ECAP_AER+0x08.L", p); + + fp = popen(cmd, "r"); + if (!fp) { + LOG("failed to read ECAP_AER+0x08 for %s\n", p); + return FPGA_EXCEPTION; + } + + sz = fread(output, 1, sizeof(output), fp); + + if (sz >= sizeof(output)) + sz = sizeof(output) - 1; + output[sz] = '\0'; + + pclose(fp); + + vc->previous_ecap_aer[0] = strtoul(output, NULL, 16); + + LOG("saving previous ECAP_AER+0x08 value 0x%08x for %s\n", + vc->previous_ecap_aer[0], p); + + + snprintf(cmd, sizeof(cmd), + "setpci -s %s ECAP_AER+0x14.L", p); + + fp = popen(cmd, "r"); + if (!fp) { + LOG("failed to read ECAP_AER+0x14 for %s\n", p); + return FPGA_EXCEPTION; + } + + sz = fread(output, 1, sizeof(output), fp); + + if (sz >= sizeof(output)) + sz = sizeof(output) - 1; + output[sz] = '\0'; + + pclose(fp); + + vc->previous_ecap_aer[1] = strtoul(output, NULL, 16); + + LOG("saving previous ECAP_AER+0x14 value 0x%08x for %s\n", + vc->previous_ecap_aer[1], p); + + + // Disable AER. + + snprintf(cmd, sizeof(cmd), + "setpci -s %s ECAP_AER+0x08.L=0xffffffff", p); + + fp = popen(cmd, "r"); + if (!fp) { + LOG("failed to write ECAP_AER+0x08 for %s\n", p); + return FPGA_EXCEPTION; + } + + pclose(fp); + + snprintf(cmd, sizeof(cmd), + "setpci -s %s ECAP_AER+0x14.L=0xffffffff", p); + + fp = popen(cmd, "r"); + if (!fp) { + LOG("failed to write ECAP_AER+0x14 for %s\n", p); + return FPGA_EXCEPTION; + } + + pclose(fp); + + return FPGA_OK; +} + +STATIC fpga_result vc_enable_aer(vc_device *vc) +{ + fpga_token token; + fpga_result res; + fpga_properties prop = NULL; + char path[PATH_MAX]; + char rlpath[PATH_MAX]; + char *p; + char cmd[256]; + FILE *fp; + + uint16_t seg = 0; + uint8_t bus = 0; + uint8_t dev = 0; + uint8_t fn = 0; + + token = vc->base_device->token; + + res = fpgaGetProperties(token, &prop); + if (res != FPGA_OK) { + LOG("failed to get fpga properties.\n"); + return res; + } + + if ((fpgaPropertiesGetSegment(prop, &seg) != FPGA_OK) || + (fpgaPropertiesGetBus(prop, &bus) != FPGA_OK) || + (fpgaPropertiesGetDevice(prop, &dev) != FPGA_OK) || + (fpgaPropertiesGetFunction(prop, &fn) != FPGA_OK)) { + LOG("failed to get PCI attributes.\n"); + fpgaDestroyProperties(&prop); + return FPGA_EXCEPTION; + } + + fpgaDestroyProperties(&prop); + + snprintf(path, sizeof(path), + "/sys/bus/pci/devices/%04x:%02x:%02x.%d", + (int)seg, (int)bus, (int)dev, (int)fn); + + memset(rlpath, 0, sizeof(rlpath)); + + if (readlink(path, rlpath, sizeof(rlpath)) < 0) { + LOG("readlink \"%s\" failed.\n", path); + return FPGA_EXCEPTION; + } + + // (rlpath) + // 1111111111 + // 01234567890123456789 + // ../../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.0/ + // 0000:b0:09.0/0000:b2:00.0 + + p = strstr(rlpath, "devices/pci"); + + p += 19; + *(p + 12) = '\0'; + + // Write the saved ECAP_AER values to enable AER. + + snprintf(cmd, sizeof(cmd), + "setpci -s %s ECAP_AER+0x08.L=0x%08x", + p, vc->previous_ecap_aer[0]); + + fp = popen(cmd, "r"); + if (!fp) { + LOG("failed to write ECAP_AER+0x08 for %s\n", p); + return FPGA_EXCEPTION; + } + + pclose(fp); + + LOG("restored previous ECAP_AER+0x08 value 0x%08x for %s\n", + vc->previous_ecap_aer[0], p); + + + snprintf(cmd, sizeof(cmd), + "setpci -s %s ECAP_AER+0x14.L=0x%08x", + p, vc->previous_ecap_aer[1]); + + fp = popen(cmd, "r"); + if (!fp) { + LOG("failed to write ECAP_AER+0x14 for %s\n", p); + return FPGA_EXCEPTION; + } + + pclose(fp); + + LOG("restored previous ECAP_AER+0x14 value 0x%08x for %s\n", + vc->previous_ecap_aer[1], p); + + return FPGA_OK; +} + +STATIC bool vc_monitor_sensors(vc_device *vc) +{ + uint32_t i; + uint32_t monitoring = 0; + bool negative_trans = false; + bool res = true; + + for (i = 0 ; i < vc->num_sensors ; ++i) { + vc_sensor *s = &vc->sensors[i]; + + if (s->flags & FPGAD_SENSOR_VC_IGNORE) + continue; + + if (s->flags & (FPGAD_SENSOR_VC_HIGH_WARN_VALID| + FPGAD_SENSOR_VC_LOW_WARN_VALID)) + ++monitoring; + + if (fpgaObjectRead64(s->value_object, + &s->value, + FPGA_OBJECT_SYNC) != FPGA_OK) { + if (++s->read_errors >= + FPGAD_SENSOR_VC_MAX_READ_ERRORS) + s->flags |= FPGAD_SENSOR_VC_IGNORE; + continue; + } + + if (HIGH_WARN(s) || LOW_WARN(s)) { + opae_api_send_EVENT_POWER_THERMAL(vc->base_device); + BIT_SET_SET(vc->state_tripped, s->id); + if (!BIT_IS_SET(vc->state_last, s->id)) { + LOG("sensor '%s' warning.\n", s->name); + if (!vc->aer_disabled) { + if (FPGA_OK == vc_disable_aer(vc)) + vc->aer_disabled = true; + } + } + } + + if (HIGH_NORMAL(s) || LOW_NORMAL(s)) { + if (BIT_IS_SET(vc->state_last, s->id)) { + negative_trans = true; + LOG("sensor '%s' back to normal.\n", s->name); + } + } + + if (BIT_IS_SET(vc->state_last, s->id) && + BIT_IS_SET(vc->state_tripped, s->id)) { + LOG_MOD(vc->tripped_count, + "sensor '%s' still tripped.\n", s->name); + } + } + + if (negative_trans) { + for (i = 0 ; i < vc->num_sensors ; ++i) { + if (BIT_IS_SET(vc->state_tripped, vc->sensors[i].id)) + break; + } + + if (i == vc->num_sensors) { + // no remaining tripped sensors + vc->tripped_count = 0; + if (vc->aer_disabled) { + if (FPGA_OK == vc_enable_aer(vc)) + vc->aer_disabled = false; + } + } + } + + /* + ** Are we still monitoring any sensor that has a valid + ** high/low warn threshold? If not, then this fn should + ** return false so that we wait for sensor enumeration + ** in the caller. It's possible that we're ignoring all + ** of the sensors because they became unavailable due to + ** driver removal, eg. + */ + if (!monitoring) + res = false; + + for (i = 0 ; i < vc->num_sensors ; ++i) { + vc_sensor *s = &vc->sensors[i]; + if (BIT_IS_SET(vc->state_tripped, s->id)) + BIT_SET_SET(vc->state_last, s->id); + else + BIT_SET_CLR(vc->state_last, s->id); + } + + memset(vc->state_tripped, 0, (vc->max_sensor_id + 7) / 8); + + return res; +} + +STATIC int cool_down = 30; + +STATIC void *monitor_fme_vc_thread(void *arg) +{ + fpgad_monitored_device *d = + (fpgad_monitored_device *)arg; + + vc_device *vc = (vc_device *)d->thread_context; + + uint32_t enum_retries = 0; + uint8_t *save_state_last = NULL; + + while (vc_threads_running) { + + while (vc_enum_sensors(vc) != FPGA_OK) { + LOG_MOD(enum_retries, "waiting to enumerate sensors.\n"); + if (!vc_threads_running) + return NULL; + sleep(1); + } + + if (save_state_last) { + free(vc->state_last); + vc->state_last = save_state_last; + save_state_last = NULL; + } + + while (vc_monitor_sensors(vc)) { + usleep(d->config->poll_interval_usec); + + if (!vc_threads_running) { + vc_destroy_sensors(vc); + return NULL; + } + } + + save_state_last = vc->state_last; + vc->state_last = NULL; + + vc_destroy_sensors(vc); + + } + + return NULL; +} + +STATIC int vc_parse_config(vc_device *vc, const char *cfg) +{ + json_object *root; + enum json_tokener_error j_err = json_tokener_success; + json_object *j_cool_down = NULL; + json_object *j_config_sensors_enabled = NULL; + json_object *j_sensors = NULL; + int res = 1; + int sensor_entries; + int i; + + root = json_tokener_parse_verbose(cfg, &j_err); + if (!root) { + LOG("error parsing config: %s\n", + json_tokener_error_desc(j_err)); + return 1; + } + + if (!json_object_object_get_ex(root, + "cool-down", + &j_cool_down)) { + LOG("failed to find cool-down key in config.\n"); + goto out_put; + } + + if (!json_object_is_type(j_cool_down, json_type_int)) { + LOG("cool-down key not integer.\n"); + goto out_put; + } + + cool_down = json_object_get_int(j_cool_down); + if (cool_down < 0) + cool_down = 30; + + LOG("set cool-down period to %d seconds.\n", cool_down); + + res = 0; + + if (!json_object_object_get_ex(root, + "config-sensors-enabled", + &j_config_sensors_enabled)) { + LOG("failed to find config-sensors-enabled key in config.\n" + "Skipping user sensor config.\n"); + goto out_put; + } + + if (!json_object_is_type(j_config_sensors_enabled, json_type_boolean)) { + LOG("config-sensors-enabled key not Boolean.\n" + "Skipping user sensor config.\n"); + goto out_put; + } + + if (!json_object_get_boolean(j_config_sensors_enabled)) { + LOG("config-sensors-enabled key set to false.\n" + "Skipping user sensor config.\n"); + goto out_put; + } + + if (!json_object_object_get_ex(root, + "sensors", + &j_sensors)) { + LOG("failed to find sensors key in config.\n" + "Skipping user sensor config.\n"); + goto out_put; + } + + if (!json_object_is_type(j_sensors, json_type_array)) { + LOG("sensors key not array.\n" + "Skipping user sensor config.\n"); + goto out_put; + } + + sensor_entries = json_object_array_length(j_sensors); + if (!sensor_entries) { + LOG("sensors key is empty array.\n" + "Skipping user sensor config.\n"); + goto out_put; + } + + vc->config_sensors = calloc(sensor_entries, sizeof(vc_config_sensor)); + if (!vc->config_sensors) { + LOG("calloc failed. Skipping user sensor config.\n"); + goto out_put; + } + + vc->num_config_sensors = (uint32_t)sensor_entries; + + for (i = 0 ; i < sensor_entries ; ++i) { + json_object *j_sensor_sub_i = json_object_array_get_idx(j_sensors, i); + json_object *j_id; + json_object *j_high_fatal; + json_object *j_high_warn; + json_object *j_low_fatal; + json_object *j_low_warn; + + if (!json_object_object_get_ex(j_sensor_sub_i, + "id", + &j_id)) { + LOG("failed to find id key in config sensors[%d].\n" + "Skipping entry %d.\n", i, i); + vc->config_sensors[i].id = MAX_VC_SENSORS; + vc->config_sensors[i].flags = FPGAD_SENSOR_VC_IGNORE; + continue; + } + + if (!json_object_is_type(j_id, json_type_int)) { + LOG("sensors[%d].id key not int.\n" + "Skipping entry %d.\n", i, i); + vc->config_sensors[i].id = MAX_VC_SENSORS; + vc->config_sensors[i].flags = FPGAD_SENSOR_VC_IGNORE; + continue; + } + + vc->config_sensors[i].id = json_object_get_int(j_id); + + if (json_object_object_get_ex(j_sensor_sub_i, + "high-fatal", + &j_high_fatal)) { + if (json_object_is_type(j_high_fatal, + json_type_double)) { + vc->config_sensors[i].high_fatal = + (uint64_t)(json_object_get_double(j_high_fatal) + * 1000.0); + vc->config_sensors[i].flags |= + FPGAD_SENSOR_VC_HIGH_FATAL_VALID; + LOG("user sensor%u high-fatal: %f\n", + vc->config_sensors[i].id, + json_object_get_double(j_high_fatal)); + } + } + + if (json_object_object_get_ex(j_sensor_sub_i, + "high-warn", + &j_high_warn)) { + if (json_object_is_type(j_high_warn, + json_type_double)) { + vc->config_sensors[i].high_warn = + (uint64_t)(json_object_get_double(j_high_warn) + * 1000.0); + vc->config_sensors[i].flags |= + FPGAD_SENSOR_VC_HIGH_WARN_VALID; + LOG("user sensor%u high-warn: %f\n", + vc->config_sensors[i].id, + json_object_get_double(j_high_warn)); + } + } + + if (json_object_object_get_ex(j_sensor_sub_i, + "low-fatal", + &j_low_fatal)) { + if (json_object_is_type(j_low_fatal, + json_type_double)) { + vc->config_sensors[i].low_fatal = + (uint64_t)(json_object_get_double(j_low_fatal) + * 1000.0); + vc->config_sensors[i].flags |= + FPGAD_SENSOR_VC_LOW_FATAL_VALID; + LOG("user sensor%u low-fatal: %f\n", + vc->config_sensors[i].id, + json_object_get_double(j_low_fatal)); + } + } + + if (json_object_object_get_ex(j_sensor_sub_i, + "low-warn", + &j_low_warn)) { + if (json_object_is_type(j_low_warn, + json_type_double)) { + vc->config_sensors[i].low_warn = + (uint64_t)(json_object_get_double(j_low_warn) + * 1000.0); + vc->config_sensors[i].flags |= + FPGAD_SENSOR_VC_LOW_WARN_VALID; + LOG("user sensor%u low-warn: %f\n", + vc->config_sensors[i].id, + json_object_get_double(j_low_warn)); + } + } + } + +out_put: + json_object_put(root); + return res; +} + +int fpgad_plugin_configure(fpgad_monitored_device *d, + const char *cfg) +{ + int res = 1; + vc_device *vc; + + vc_threads_running = true; + + d->type = FPGAD_PLUGIN_TYPE_THREAD; + + if (d->object_type == FPGA_DEVICE) { + + d->thread_fn = monitor_fme_vc_thread; + d->thread_stop_fn = stop_vc_threads; + + vc = calloc(1, sizeof(vc_device)); + if (!vc) + return res; + + vc->base_device = d; + d->thread_context = vc; + + LOG("monitoring vid=0x%04x did=0x%04x (%s)\n", + d->supported->vendor_id, + d->supported->device_id, + d->object_type == FPGA_ACCELERATOR ? + "accelerator" : "device"); + + res = vc_parse_config(vc, cfg); + if (res) { + free(vc); + } + + } + + // Not currently monitoring the Port device + + return res; +} + +void fpgad_plugin_destroy(fpgad_monitored_device *d) +{ + LOG("stop monitoring vid=0x%04x did=0x%04x (%s)\n", + d->supported->vendor_id, + d->supported->device_id, + d->object_type == FPGA_ACCELERATOR ? + "accelerator" : "device"); + + if (d->thread_context) { + vc_destroy_device((vc_device *)d->thread_context); + free(d->thread_context); + d->thread_context = NULL; + } +} diff --git a/tools/fpgad/plugins/fpgad-xfpga/CMakeLists.txt b/tools/fpgad/plugins/fpgad-xfpga/CMakeLists.txt new file mode 100644 index 0000000..fd8c13d --- /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 0000000..fa5846b --- /dev/null +++ b/tools/fpgad/plugins/fpgad-xfpga/fpgad-xfpga.c @@ -0,0 +1,992 @@ +// Copyright(c) 2018-2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include "fpgad/api/opae_events_api.h" +#include "fpgad/api/device_monitoring.h" + +#ifdef LOG +#undef LOG +#endif +#define LOG(format, ...) \ +log_printf("fpgad-xfpga: " format, ##__VA_ARGS__) + +enum fpga_power_state { + FPGAD_NORMAL_PWR = 0, + FPGAD_AP1_STATE, + FPGAD_AP2_STATE, + FPGAD_AP6_STATE +}; + +typedef struct _fpgad_xfpga_AP_context { + const char *sysfs_file; + const char *message; + int low_bit; + int high_bit; +} fpgad_xfpga_AP_context; + +fpgad_xfpga_AP_context fpgad_xfpga_AP_contexts[] = { + { "ap1_event", "AP1 Triggered!", 0, 0 }, + { "ap2_event", "AP2 Triggered!", 0, 0 }, + { "power_state", "Power state changed to", 0, 1 }, +}; + +fpgad_detection_status +fpgad_xfpga_detect_AP1_or_AP2(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_AP_context *c = + (fpgad_xfpga_AP_context *)context; + fpga_object obj = NULL; + fpga_result res; + uint64_t err = 0; + uint64_t mask; + uint64_t value; + int i; + bool detected = false; + + res = fpgaTokenGetObject(d->token, c->sysfs_file, + &obj, 0); + if (res != FPGA_OK) { + LOG("failed to get error object\n"); + return FPGAD_STATUS_NOT_DETECTED; + } + + res = fpgaObjectRead64(obj, &err, 0); + if (res != FPGA_OK) { + LOG("failed to read error object\n"); + fpgaDestroyObject(&obj); + return FPGAD_STATUS_NOT_DETECTED; + } + + fpgaDestroyObject(&obj); + + mask = 0; + for (i = c->low_bit ; i <= c->high_bit ; ++i) + mask |= 1ULL << i; + + value = mask & err; + + if (value != 0 && !mon_has_error_occurred(d, context)) { + detected = mon_add_device_error(d, context); + } + + if (value == 0 && mon_has_error_occurred(d, context)) { + mon_remove_device_error(d, context); + } + + return detected ? FPGAD_STATUS_DETECTED : FPGAD_STATUS_NOT_DETECTED; +} + +void fpgad_xfpga_respond_AP1_or_AP2(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_AP_context *c = + (fpgad_xfpga_AP_context *)context; + + LOG("%s\n", c->message); + + // Signal OPAE events API + opae_api_send_EVENT_POWER_THERMAL(d); +} + +fpgad_detection_status +fpgad_xfpga_detect_PowerStateChange(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_AP_context *c = + (fpgad_xfpga_AP_context *)context; + fpga_object obj = NULL; + fpga_result res; + uint64_t err = 0; + uint64_t mask; + uint64_t value; + int i; + bool detected = false; + + res = fpgaTokenGetObject(d->token, c->sysfs_file, + &obj, 0); + if (res != FPGA_OK) { + LOG("failed to get error object\n"); + return FPGAD_STATUS_NOT_DETECTED; + } + + res = fpgaObjectRead64(obj, &err, 0); + if (res != FPGA_OK) { + LOG("failed to read error object\n"); + fpgaDestroyObject(&obj); + return FPGAD_STATUS_NOT_DETECTED; + } + + fpgaDestroyObject(&obj); + + mask = 0; + for (i = c->low_bit ; i <= c->high_bit ; ++i) + mask |= 1ULL << i; + + value = mask & err; + + if (value != d->scratchpad[0]) { + detected = true; + } + + d->scratchpad[0] = value; + + return detected ? FPGAD_STATUS_DETECTED : FPGAD_STATUS_NOT_DETECTED; +} + +void fpgad_xfpga_respond_PowerStateChange(fpgad_monitored_device *d, + void *context) +{ + const char *power_states[] = { + "Normal Power", + "AP1 Power State", + "AP2 Power State", + "AP6 Power State" + }; + + fpgad_xfpga_AP_context *c = + (fpgad_xfpga_AP_context *)context; + + LOG("%s %s\n", c->message, + d->scratchpad[0] < 4 ? + power_states[d->scratchpad[0]] : + "unknown"); +} + +typedef struct _fpgad_xfpga_Error_context { + const char *sysfs_file; + const char *message; + int low_bit; + int high_bit; +} fpgad_xfpga_Error_context; + +fpgad_xfpga_Error_context fpgad_xfpga_Error_contexts[] = { + /* 0 */ { "errors/errors", "PORT_ERROR[0x1010].VfFlrAccessError", 51, 51 }, + /* 1 */ { "errors/errors", "PORT_ERROR[0x1010].Ap6Event", 50, 50 }, /* AP6 NULL GBS */ + /* 2 */ { "errors/errors", "PORT_ERROR[0x1010].PMRError", 49, 49 }, + /* 3 */ { "errors/errors", "PORT_ERROR[0x1010].PageFault", 48, 48 }, + /* 4 */ { "errors/errors", "PORT_ERROR[0x1010].VgaMemRangeError", 47, 47 }, + /* 5 */ { "errors/errors", "PORT_ERROR[0x1010].LegRangeHighError", 46, 46 }, + /* 6 */ { "errors/errors", "PORT_ERROR[0x1010].LegRangeLowError", 45, 45 }, + /* 7 */ { "errors/errors", "PORT_ERROR[0x1010].GenProtRangeError", 44, 44 }, + /* 8 */ { "errors/errors", "PORT_ERROR[0x1010].L1prMesegError", 43, 43 }, + /* 9 */ { "errors/errors", "PORT_ERROR[0x1010].L1prSmrr2Error", 42, 42 }, + /* 10 */ { "errors/errors", "PORT_ERROR[0x1010].L1prSmrrError", 41, 41 }, + /* 11 */ { "errors/errors", "PORT_ERROR[0x1010].TxReqCounterOverflow", 40, 40 }, + /* 12 */ { "errors/errors", "PORT_ERROR[0x1010].UnexpMMIOResp", 34, 34 }, + /* 13 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh2FifoOverflow", 33, 33 }, + /* 14 */ { "errors/errors", "PORT_ERROR[0x1010].MMIOTimedOut", 32, 32 }, + /* 15 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1NonZeroSOP", 24, 24 }, + /* 16 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1IncorrectAddr", 23, 23 }, + /* 17 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1DataPayloadOverrun", 22, 22 }, + /* 18 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1InsufficientData", 21, 21 }, + /* 19 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Len4NotAligned", 20, 20 }, + /* 20 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Len2NotAligned", 19, 19 }, + /* 21 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Len3NotSupported", 18, 18 }, + /* 22 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1InvalidReqEncoding", 17, 17 }, + /* 23 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh1Overflow", 16, 16 }, + /* 24 */ { "errors/errors", "PORT_ERROR[0x1010].MMIOWrWhileRst", 10, 10 }, + /* 25 */ { "errors/errors", "PORT_ERROR[0x1010].MMIORdWhileRst", 9, 9 }, + /* 26 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Len4NotAligned", 4, 4 }, + /* 27 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Len2NotAligned", 3, 3 }, + /* 28 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Len3NotSupported", 2, 2 }, + /* 29 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0InvalidReqEncoding", 1, 1 }, + /* 30 */ { "errors/errors", "PORT_ERROR[0x1010].TxCh0Overflow", 0, 0 }, + + /* 31 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxReqCounterOverflow", 40, 40 }, + /* 32 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh2FifoOverflow", 33, 33 }, + /* 33 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].MMIOTimedOut", 32, 32 }, + /* 34 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1IllegalVCsel", 25, 25 }, + /* 35 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1NonZeroSOP", 24, 24 }, + /* 36 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1IncorrectAddr", 23, 23 }, + /* 37 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1DataPayloadOverrun", 22, 22 }, + /* 38 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1InsufficientData", 21, 21 }, + /* 39 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Len4NotAligned", 20, 20 }, + /* 40 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Len2NotAligned", 19, 19 }, + /* 41 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Len3NotSupported", 18, 18 }, + /* 42 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1InvalidReqEncoding", 17, 17 }, + /* 43 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh1Overflow", 16, 16 }, + /* 44 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].MMIOWrWhileRst", 10, 10 }, + /* 45 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].MMIORdWhileRst", 9, 9 }, + /* 46 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Len4NotAligned", 4, 4 }, + /* 47 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Len2NotAligned", 3, 3 }, + /* 48 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Len3NotSupported", 2, 2 }, + /* 49 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0InvalidReqEncoding", 1, 1 }, + /* 50 */ { "errors/first_error", "PORT_FIRST_ERROR[0x1018].TxCh0Overflow", 0, 0 }, + + /* 51 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].CvlCdcParErro0", 17, 19 }, + /* 52 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].Pcie1CdcParErr", 12, 16 }, + /* 53 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].Pcie0CdcParErr", 7, 11 }, + /* 54 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].MBPErr", 6, 6 }, + /* 55 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].AfuAccessModeErr", 5, 5 }, + /* 56 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].IommuParityErr", 4, 4 }, + /* 57 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].KtiCdcParityErr", 2, 3 }, + /* 58 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].FabricFifoUOflow", 1, 1 }, + /* 59 */ { "errors/fme-errors/errors", "FME_ERROR0[0x4010].FabricErr", 0, 0 }, + + /* 60 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].FunctTypeErr", 63, 63 }, + /* 61 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].VFNumb", 62, 62 }, + /* 62 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].RxPoisonTlpErr", 9, 9 }, + /* 63 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].ParityErr", 8, 8 }, + /* 64 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].CompTimeOutErr", 7, 7 }, + /* 65 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].CompStatErr", 6, 6 }, + /* 66 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].CompTagErr", 5, 5 }, + /* 67 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MRLengthErr", 4, 4 }, + /* 68 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MRAddrErr", 3, 3 }, + /* 69 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MWLengthErr", 2, 2 }, + /* 70 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].MWAddrErr", 1, 1 }, + /* 71 */ { "errors/pcie0_errors", "PCIE0_ERROR[0x4020].FormatTypeErr", 0, 0 }, + + /* 72 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].RxPoisonTlpErr", 9, 9 }, + /* 73 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].ParityErr", 8, 8 }, + /* 74 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].CompTimeOutErr", 7, 7 }, + /* 75 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].CompStatErr", 6, 6 }, + /* 76 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].CompTagErr", 5, 5 }, + /* 77 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MRLengthErr", 4, 4 }, + /* 78 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MRAddrErr", 3, 3 }, + /* 79 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MWLengthErr", 2, 2 }, + /* 80 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].MWAddrErr", 1, 1 }, + /* 81 */ { "errors/pcie1_errors", "PCIE1_ERROR[0x4030].FormatTypeErr", 0, 0 }, + + /* 82 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].MBPErr", 12, 12 }, + /* 83 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PowerThreshAP2", 11, 11 }, + /* 84 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PowerThreshAP1", 10, 10 }, + /* 85 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].TempThreshAP6", 9, 9 }, /* AP6 */ + /* 86 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].InjectedWarningErr", 6, 6 }, + /* 87 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].AfuAccessModeErr", 5, 5 }, + /* 88 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].ProcHot", 4, 4 }, + /* 89 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PortFatalErr", 3, 3 }, + /* 90 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].PcieError", 2, 2 }, + /* 91 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].TempThreshAP2", 1, 1 }, + /* 92 */ { "errors/nonfatal_errors", "RAS_NOFAT_ERR_STAT[0x4050].TempThreshAP1", 0, 0 }, + + /* 93 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].InjectedCatastErr", 11, 11 }, + /* 94 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].ThermCatastErr", 10, 10 }, + /* 95 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].CrcCatastErr", 9, 9 }, + /* 96 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].InjectedFatalErr", 8, 8 }, + /* 97 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].PciePoisonErr", 7, 7 }, + /* 98 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].FabricFatalErr", 6, 6 }, + /* 99 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].IommuFatalErr", 5, 5 }, + /*100 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].DramFatalErr", 4, 4 }, + /*101 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].KtiProtoFatalErr", 3, 3 }, + /*102 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].CciFatalErr", 2, 2 }, + /*103 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].TagCchFatalErr", 1, 1 }, + /*104 */ { "errors/catfatal_errors", "RAS_CATFAT_ERROR_STAT[0x4060].KtiLinkFatalErr", 0, 0 }, +}; + +fpgad_detection_status +fpgad_xfpga_detect_Error(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_Error_context *c = + (fpgad_xfpga_Error_context *)context; + fpga_object obj = NULL; + fpga_result res; + uint64_t err = 0; + uint64_t mask; + uint64_t value; + int i; + bool detected = false; + + res = fpgaTokenGetObject(d->token, c->sysfs_file, + &obj, 0); + if (res != FPGA_OK) { + LOG("failed to get error object\n"); + return FPGAD_STATUS_NOT_DETECTED; + } + + res = fpgaObjectRead64(obj, &err, 0); + if (res != FPGA_OK) { + LOG("failed to read error object\n"); + fpgaDestroyObject(&obj); + return FPGAD_STATUS_NOT_DETECTED; + } + + fpgaDestroyObject(&obj); + + mask = 0; + for (i = c->low_bit ; i <= c->high_bit ; ++i) + mask |= 1ULL << i; + + value = mask & err; + + if (value != 0 && !mon_has_error_occurred(d, context)) { + detected = mon_add_device_error(d, context); + } + + if (value == 0 && mon_has_error_occurred(d, context)) { + mon_remove_device_error(d, context); + } + + return detected ? FPGAD_STATUS_DETECTED : FPGAD_STATUS_NOT_DETECTED; +} + +fpgad_detection_status +fpgad_xfpga_detect_High_Priority_Error(fpgad_monitored_device *d, + void *context) +{ + fpgad_detection_status status; + + status = fpgad_xfpga_detect_Error(d, context); + + if (status == FPGAD_STATUS_DETECTED) + return FPGAD_STATUS_DETECTED_HIGH; + + return status; +} + +void fpgad_xfpga_respond_LogError(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_Error_context *c = + (fpgad_xfpga_Error_context *)context; + + LOG("%s\n", c->message); + + // signal OPAE events API + opae_api_send_EVENT_ERROR(d); +} + +void fpgad_xfpga_respond_AP6_and_Null_GBS(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_Error_context *c = + (fpgad_xfpga_Error_context *)context; + + LOG("%s\n", c->message); + + // Program NULL GBS + + // d will be the Port device. We need to find the parent + // (FME) device to perform the PR. + + if (d->bitstr) { + fpga_result res; + fpga_properties prop = NULL; + fpga_token fme_tok = NULL; + fpga_handle fme_h = NULL; + const uint32_t slot = 0; + + res = fpgaGetProperties(d->token, &prop); + if (res != FPGA_OK) { + LOG("(AP6) failed to get properties! : %s\n", + fpgaErrStr(res)); + goto out_signal; + } + + res = fpgaPropertiesGetParent(prop, &fme_tok); + if (res != FPGA_OK || !fme_tok) { + LOG("(AP6) failed to get FME token! : %s\n", + fpgaErrStr(res)); + goto out_destroy_props; + } + + res = fpgaOpen(fme_tok, &fme_h, 0); + if (res != FPGA_OK) { + LOG("(AP6) failed to get FME handle! : %s\n", + fpgaErrStr(res)); + goto out_destroy_fme_tok; + } + + LOG("programming \"%s\": ", d->bitstr->filename); + + res = fpgaReconfigureSlot(fme_h, + slot, + d->bitstr->data, + d->bitstr->data_len, + FPGA_RECONF_FORCE); + if (res != FPGA_OK) + LOG("SUCCESS\n"); + else + LOG("FAILED : %s\n", fpgaErrStr(res)); + + fpgaClose(fme_h); +out_destroy_fme_tok: + fpgaDestroyToken(&fme_tok); +out_destroy_props: + fpgaDestroyProperties(&prop); + } else + LOG("no bitstream to program for AP6!\n"); + + // Signal OPAE events API +out_signal: + opae_api_send_EVENT_POWER_THERMAL(d); +} + +void fpgad_xfpga_respond_AP6(fpgad_monitored_device *d, + void *context) +{ + fpgad_xfpga_Error_context *c = + (fpgad_xfpga_Error_context *)context; + + LOG("%s\n", c->message); + + // Signal OPAE events API + opae_api_send_EVENT_POWER_THERMAL(d); +} + +// Port detections +STATIC fpgad_detect_event_t fpgad_xfpga_port_detections[] = { + fpgad_xfpga_detect_AP1_or_AP2, + fpgad_xfpga_detect_AP1_or_AP2, + fpgad_xfpga_detect_PowerStateChange, + + fpgad_xfpga_detect_Error, // 0 + fpgad_xfpga_detect_High_Priority_Error, // 1 (AP6 and NULL GBS) + fpgad_xfpga_detect_Error, // 2 + fpgad_xfpga_detect_Error, // 3 + fpgad_xfpga_detect_Error, // 4 + fpgad_xfpga_detect_Error, // 5 + fpgad_xfpga_detect_Error, // 6 + fpgad_xfpga_detect_Error, // 7 + fpgad_xfpga_detect_Error, // 8 + fpgad_xfpga_detect_Error, // 9 + fpgad_xfpga_detect_Error, // 10 + fpgad_xfpga_detect_Error, // 11 + fpgad_xfpga_detect_Error, // 12 + fpgad_xfpga_detect_Error, // 13 + fpgad_xfpga_detect_Error, // 14 + fpgad_xfpga_detect_Error, // 15 + fpgad_xfpga_detect_Error, // 16 + fpgad_xfpga_detect_Error, // 17 + fpgad_xfpga_detect_Error, // 18 + fpgad_xfpga_detect_Error, // 19 + fpgad_xfpga_detect_Error, // 20 + fpgad_xfpga_detect_Error, // 21 + fpgad_xfpga_detect_Error, // 22 + fpgad_xfpga_detect_Error, // 23 + fpgad_xfpga_detect_Error, // 24 + fpgad_xfpga_detect_Error, // 25 + fpgad_xfpga_detect_Error, // 26 + fpgad_xfpga_detect_Error, // 27 + fpgad_xfpga_detect_Error, // 28 + fpgad_xfpga_detect_Error, // 29 + fpgad_xfpga_detect_Error, // 30 + + fpgad_xfpga_detect_Error, // 31 + fpgad_xfpga_detect_Error, // 32 + fpgad_xfpga_detect_Error, // 33 + fpgad_xfpga_detect_Error, // 34 + fpgad_xfpga_detect_Error, // 35 + fpgad_xfpga_detect_Error, // 36 + fpgad_xfpga_detect_Error, // 37 + fpgad_xfpga_detect_Error, // 38 + fpgad_xfpga_detect_Error, // 39 + fpgad_xfpga_detect_Error, // 40 + fpgad_xfpga_detect_Error, // 41 + fpgad_xfpga_detect_Error, // 42 + fpgad_xfpga_detect_Error, // 43 + fpgad_xfpga_detect_Error, // 44 + fpgad_xfpga_detect_Error, // 45 + fpgad_xfpga_detect_Error, // 46 + fpgad_xfpga_detect_Error, // 47 + fpgad_xfpga_detect_Error, // 48 + fpgad_xfpga_detect_Error, // 49 + fpgad_xfpga_detect_Error, // 50 + + NULL +}; + +STATIC void *fpgad_xfpga_port_detection_contexts[] = { + &fpgad_xfpga_AP_contexts[0], + &fpgad_xfpga_AP_contexts[1], + &fpgad_xfpga_AP_contexts[2], + + &fpgad_xfpga_Error_contexts[0], + &fpgad_xfpga_Error_contexts[1], + &fpgad_xfpga_Error_contexts[2], + &fpgad_xfpga_Error_contexts[3], + &fpgad_xfpga_Error_contexts[4], + &fpgad_xfpga_Error_contexts[5], + &fpgad_xfpga_Error_contexts[6], + &fpgad_xfpga_Error_contexts[7], + &fpgad_xfpga_Error_contexts[8], + &fpgad_xfpga_Error_contexts[9], + &fpgad_xfpga_Error_contexts[10], + &fpgad_xfpga_Error_contexts[11], + &fpgad_xfpga_Error_contexts[12], + &fpgad_xfpga_Error_contexts[13], + &fpgad_xfpga_Error_contexts[14], + &fpgad_xfpga_Error_contexts[15], + &fpgad_xfpga_Error_contexts[16], + &fpgad_xfpga_Error_contexts[17], + &fpgad_xfpga_Error_contexts[18], + &fpgad_xfpga_Error_contexts[19], + &fpgad_xfpga_Error_contexts[20], + &fpgad_xfpga_Error_contexts[21], + &fpgad_xfpga_Error_contexts[22], + &fpgad_xfpga_Error_contexts[23], + &fpgad_xfpga_Error_contexts[24], + &fpgad_xfpga_Error_contexts[25], + &fpgad_xfpga_Error_contexts[26], + &fpgad_xfpga_Error_contexts[27], + &fpgad_xfpga_Error_contexts[28], + &fpgad_xfpga_Error_contexts[29], + &fpgad_xfpga_Error_contexts[30], + + &fpgad_xfpga_Error_contexts[31], + &fpgad_xfpga_Error_contexts[32], + &fpgad_xfpga_Error_contexts[33], + &fpgad_xfpga_Error_contexts[34], + &fpgad_xfpga_Error_contexts[35], + &fpgad_xfpga_Error_contexts[36], + &fpgad_xfpga_Error_contexts[37], + &fpgad_xfpga_Error_contexts[38], + &fpgad_xfpga_Error_contexts[39], + &fpgad_xfpga_Error_contexts[40], + &fpgad_xfpga_Error_contexts[41], + &fpgad_xfpga_Error_contexts[42], + &fpgad_xfpga_Error_contexts[43], + &fpgad_xfpga_Error_contexts[44], + &fpgad_xfpga_Error_contexts[45], + &fpgad_xfpga_Error_contexts[46], + &fpgad_xfpga_Error_contexts[47], + &fpgad_xfpga_Error_contexts[48], + &fpgad_xfpga_Error_contexts[49], + &fpgad_xfpga_Error_contexts[50], + + NULL +}; + +// Port responses +STATIC fpgad_respond_event_t fpgad_xfpga_port_responses[] = { + fpgad_xfpga_respond_AP1_or_AP2, + fpgad_xfpga_respond_AP1_or_AP2, + fpgad_xfpga_respond_PowerStateChange, + + fpgad_xfpga_respond_LogError, // 0 + fpgad_xfpga_respond_AP6_and_Null_GBS, // 1 + fpgad_xfpga_respond_LogError, // 2 + fpgad_xfpga_respond_LogError, // 3 + fpgad_xfpga_respond_LogError, // 4 + fpgad_xfpga_respond_LogError, // 5 + fpgad_xfpga_respond_LogError, // 6 + fpgad_xfpga_respond_LogError, // 7 + fpgad_xfpga_respond_LogError, // 8 + fpgad_xfpga_respond_LogError, // 9 + fpgad_xfpga_respond_LogError, // 10 + fpgad_xfpga_respond_LogError, // 11 + fpgad_xfpga_respond_LogError, // 12 + fpgad_xfpga_respond_LogError, // 13 + fpgad_xfpga_respond_LogError, // 14 + fpgad_xfpga_respond_LogError, // 15 + fpgad_xfpga_respond_LogError, // 16 + fpgad_xfpga_respond_LogError, // 17 + fpgad_xfpga_respond_LogError, // 18 + fpgad_xfpga_respond_LogError, // 19 + fpgad_xfpga_respond_LogError, // 20 + fpgad_xfpga_respond_LogError, // 21 + fpgad_xfpga_respond_LogError, // 22 + fpgad_xfpga_respond_LogError, // 23 + fpgad_xfpga_respond_LogError, // 24 + fpgad_xfpga_respond_LogError, // 25 + fpgad_xfpga_respond_LogError, // 26 + fpgad_xfpga_respond_LogError, // 27 + fpgad_xfpga_respond_LogError, // 28 + fpgad_xfpga_respond_LogError, // 29 + fpgad_xfpga_respond_LogError, // 30 + + fpgad_xfpga_respond_LogError, // 31 + fpgad_xfpga_respond_LogError, // 32 + fpgad_xfpga_respond_LogError, // 33 + fpgad_xfpga_respond_LogError, // 34 + fpgad_xfpga_respond_LogError, // 35 + fpgad_xfpga_respond_LogError, // 36 + fpgad_xfpga_respond_LogError, // 37 + fpgad_xfpga_respond_LogError, // 38 + fpgad_xfpga_respond_LogError, // 39 + fpgad_xfpga_respond_LogError, // 40 + fpgad_xfpga_respond_LogError, // 41 + fpgad_xfpga_respond_LogError, // 42 + fpgad_xfpga_respond_LogError, // 43 + fpgad_xfpga_respond_LogError, // 44 + fpgad_xfpga_respond_LogError, // 45 + fpgad_xfpga_respond_LogError, // 46 + fpgad_xfpga_respond_LogError, // 47 + fpgad_xfpga_respond_LogError, // 48 + fpgad_xfpga_respond_LogError, // 49 + fpgad_xfpga_respond_LogError, // 50 + + NULL +}; + +STATIC void *fpgad_xfpga_port_response_contexts[] = { + &fpgad_xfpga_AP_contexts[0], + &fpgad_xfpga_AP_contexts[1], + &fpgad_xfpga_AP_contexts[2], + + &fpgad_xfpga_Error_contexts[0], + &fpgad_xfpga_Error_contexts[1], + &fpgad_xfpga_Error_contexts[2], + &fpgad_xfpga_Error_contexts[3], + &fpgad_xfpga_Error_contexts[4], + &fpgad_xfpga_Error_contexts[5], + &fpgad_xfpga_Error_contexts[6], + &fpgad_xfpga_Error_contexts[7], + &fpgad_xfpga_Error_contexts[8], + &fpgad_xfpga_Error_contexts[9], + &fpgad_xfpga_Error_contexts[10], + &fpgad_xfpga_Error_contexts[11], + &fpgad_xfpga_Error_contexts[12], + &fpgad_xfpga_Error_contexts[13], + &fpgad_xfpga_Error_contexts[14], + &fpgad_xfpga_Error_contexts[15], + &fpgad_xfpga_Error_contexts[16], + &fpgad_xfpga_Error_contexts[17], + &fpgad_xfpga_Error_contexts[18], + &fpgad_xfpga_Error_contexts[19], + &fpgad_xfpga_Error_contexts[20], + &fpgad_xfpga_Error_contexts[21], + &fpgad_xfpga_Error_contexts[22], + &fpgad_xfpga_Error_contexts[23], + &fpgad_xfpga_Error_contexts[24], + &fpgad_xfpga_Error_contexts[25], + &fpgad_xfpga_Error_contexts[26], + &fpgad_xfpga_Error_contexts[27], + &fpgad_xfpga_Error_contexts[28], + &fpgad_xfpga_Error_contexts[29], + &fpgad_xfpga_Error_contexts[30], + + &fpgad_xfpga_Error_contexts[31], + &fpgad_xfpga_Error_contexts[32], + &fpgad_xfpga_Error_contexts[33], + &fpgad_xfpga_Error_contexts[34], + &fpgad_xfpga_Error_contexts[35], + &fpgad_xfpga_Error_contexts[36], + &fpgad_xfpga_Error_contexts[37], + &fpgad_xfpga_Error_contexts[38], + &fpgad_xfpga_Error_contexts[39], + &fpgad_xfpga_Error_contexts[40], + &fpgad_xfpga_Error_contexts[41], + &fpgad_xfpga_Error_contexts[42], + &fpgad_xfpga_Error_contexts[43], + &fpgad_xfpga_Error_contexts[44], + &fpgad_xfpga_Error_contexts[45], + &fpgad_xfpga_Error_contexts[46], + &fpgad_xfpga_Error_contexts[47], + &fpgad_xfpga_Error_contexts[48], + &fpgad_xfpga_Error_contexts[49], + &fpgad_xfpga_Error_contexts[50], + + NULL +}; + +// FME detections +STATIC fpgad_detect_event_t fpgad_xfpga_fme_detections[] = { + fpgad_xfpga_detect_Error, // 51 + fpgad_xfpga_detect_Error, // 52 + fpgad_xfpga_detect_Error, // 53 + fpgad_xfpga_detect_Error, // 54 + fpgad_xfpga_detect_Error, // 55 + fpgad_xfpga_detect_Error, // 56 + fpgad_xfpga_detect_Error, // 57 + fpgad_xfpga_detect_Error, // 58 + fpgad_xfpga_detect_Error, // 59 + + fpgad_xfpga_detect_Error, // 60 + fpgad_xfpga_detect_Error, // 61 + fpgad_xfpga_detect_Error, // 62 + fpgad_xfpga_detect_Error, // 63 + fpgad_xfpga_detect_Error, // 64 + fpgad_xfpga_detect_Error, // 65 + fpgad_xfpga_detect_Error, // 66 + fpgad_xfpga_detect_Error, // 67 + fpgad_xfpga_detect_Error, // 68 + fpgad_xfpga_detect_Error, // 69 + fpgad_xfpga_detect_Error, // 70 + fpgad_xfpga_detect_Error, // 71 + + fpgad_xfpga_detect_Error, // 72 + fpgad_xfpga_detect_Error, // 73 + fpgad_xfpga_detect_Error, // 74 + fpgad_xfpga_detect_Error, // 75 + fpgad_xfpga_detect_Error, // 76 + fpgad_xfpga_detect_Error, // 77 + fpgad_xfpga_detect_Error, // 78 + fpgad_xfpga_detect_Error, // 79 + fpgad_xfpga_detect_Error, // 80 + fpgad_xfpga_detect_Error, // 81 + + fpgad_xfpga_detect_Error, // 82 + fpgad_xfpga_detect_Error, // 83 + fpgad_xfpga_detect_Error, // 84 + fpgad_xfpga_detect_Error, // 85 + fpgad_xfpga_detect_Error, // 86 + fpgad_xfpga_detect_Error, // 87 + fpgad_xfpga_detect_Error, // 88 + fpgad_xfpga_detect_Error, // 89 + fpgad_xfpga_detect_Error, // 90 + fpgad_xfpga_detect_Error, // 91 + fpgad_xfpga_detect_Error, // 92 + + fpgad_xfpga_detect_Error, // 93 + fpgad_xfpga_detect_Error, // 94 + fpgad_xfpga_detect_Error, // 95 + fpgad_xfpga_detect_Error, // 96 + fpgad_xfpga_detect_Error, // 97 + fpgad_xfpga_detect_Error, // 98 + fpgad_xfpga_detect_Error, // 99 + fpgad_xfpga_detect_Error, //100 + fpgad_xfpga_detect_Error, //101 + fpgad_xfpga_detect_Error, //102 + fpgad_xfpga_detect_Error, //103 + fpgad_xfpga_detect_Error, //104 + + NULL +}; + +STATIC void *fpgad_xfpga_fme_detection_contexts[] = { + &fpgad_xfpga_Error_contexts[51], + &fpgad_xfpga_Error_contexts[52], + &fpgad_xfpga_Error_contexts[53], + &fpgad_xfpga_Error_contexts[54], + &fpgad_xfpga_Error_contexts[55], + &fpgad_xfpga_Error_contexts[56], + &fpgad_xfpga_Error_contexts[57], + &fpgad_xfpga_Error_contexts[58], + &fpgad_xfpga_Error_contexts[59], + + &fpgad_xfpga_Error_contexts[60], + &fpgad_xfpga_Error_contexts[61], + &fpgad_xfpga_Error_contexts[62], + &fpgad_xfpga_Error_contexts[63], + &fpgad_xfpga_Error_contexts[64], + &fpgad_xfpga_Error_contexts[65], + &fpgad_xfpga_Error_contexts[66], + &fpgad_xfpga_Error_contexts[67], + &fpgad_xfpga_Error_contexts[68], + &fpgad_xfpga_Error_contexts[69], + &fpgad_xfpga_Error_contexts[70], + &fpgad_xfpga_Error_contexts[71], + + &fpgad_xfpga_Error_contexts[72], + &fpgad_xfpga_Error_contexts[73], + &fpgad_xfpga_Error_contexts[74], + &fpgad_xfpga_Error_contexts[75], + &fpgad_xfpga_Error_contexts[76], + &fpgad_xfpga_Error_contexts[77], + &fpgad_xfpga_Error_contexts[78], + &fpgad_xfpga_Error_contexts[79], + &fpgad_xfpga_Error_contexts[80], + &fpgad_xfpga_Error_contexts[81], + + &fpgad_xfpga_Error_contexts[82], + &fpgad_xfpga_Error_contexts[83], + &fpgad_xfpga_Error_contexts[84], + &fpgad_xfpga_Error_contexts[85], + &fpgad_xfpga_Error_contexts[86], + &fpgad_xfpga_Error_contexts[87], + &fpgad_xfpga_Error_contexts[88], + &fpgad_xfpga_Error_contexts[89], + &fpgad_xfpga_Error_contexts[90], + &fpgad_xfpga_Error_contexts[91], + &fpgad_xfpga_Error_contexts[92], + + &fpgad_xfpga_Error_contexts[93], + &fpgad_xfpga_Error_contexts[94], + &fpgad_xfpga_Error_contexts[95], + &fpgad_xfpga_Error_contexts[96], + &fpgad_xfpga_Error_contexts[97], + &fpgad_xfpga_Error_contexts[98], + &fpgad_xfpga_Error_contexts[99], + &fpgad_xfpga_Error_contexts[100], + &fpgad_xfpga_Error_contexts[101], + &fpgad_xfpga_Error_contexts[102], + &fpgad_xfpga_Error_contexts[103], + &fpgad_xfpga_Error_contexts[104], + + NULL +}; + +// FME responses +STATIC fpgad_respond_event_t fpgad_xfpga_fme_responses[] = { + fpgad_xfpga_respond_LogError, // 51 + fpgad_xfpga_respond_LogError, // 52 + fpgad_xfpga_respond_LogError, // 53 + fpgad_xfpga_respond_LogError, // 54 + fpgad_xfpga_respond_LogError, // 55 + fpgad_xfpga_respond_LogError, // 56 + fpgad_xfpga_respond_LogError, // 57 + fpgad_xfpga_respond_LogError, // 58 + fpgad_xfpga_respond_LogError, // 59 + + fpgad_xfpga_respond_LogError, // 60 + fpgad_xfpga_respond_LogError, // 61 + fpgad_xfpga_respond_LogError, // 62 + fpgad_xfpga_respond_LogError, // 63 + fpgad_xfpga_respond_LogError, // 64 + fpgad_xfpga_respond_LogError, // 65 + fpgad_xfpga_respond_LogError, // 66 + fpgad_xfpga_respond_LogError, // 67 + fpgad_xfpga_respond_LogError, // 68 + fpgad_xfpga_respond_LogError, // 69 + fpgad_xfpga_respond_LogError, // 70 + fpgad_xfpga_respond_LogError, // 71 + + fpgad_xfpga_respond_LogError, // 72 + fpgad_xfpga_respond_LogError, // 73 + fpgad_xfpga_respond_LogError, // 74 + fpgad_xfpga_respond_LogError, // 75 + fpgad_xfpga_respond_LogError, // 76 + fpgad_xfpga_respond_LogError, // 77 + fpgad_xfpga_respond_LogError, // 78 + fpgad_xfpga_respond_LogError, // 79 + fpgad_xfpga_respond_LogError, // 80 + fpgad_xfpga_respond_LogError, // 81 + + fpgad_xfpga_respond_LogError, // 82 + fpgad_xfpga_respond_LogError, // 83 + fpgad_xfpga_respond_LogError, // 84 + fpgad_xfpga_respond_AP6, // 85 + fpgad_xfpga_respond_LogError, // 86 + fpgad_xfpga_respond_LogError, // 87 + fpgad_xfpga_respond_LogError, // 88 + fpgad_xfpga_respond_LogError, // 89 + fpgad_xfpga_respond_LogError, // 90 + fpgad_xfpga_respond_LogError, // 91 + fpgad_xfpga_respond_LogError, // 92 + + fpgad_xfpga_respond_LogError, // 93 + fpgad_xfpga_respond_LogError, // 94 + fpgad_xfpga_respond_LogError, // 95 + fpgad_xfpga_respond_LogError, // 96 + fpgad_xfpga_respond_LogError, // 97 + fpgad_xfpga_respond_LogError, // 98 + fpgad_xfpga_respond_LogError, // 99 + fpgad_xfpga_respond_LogError, //100 + fpgad_xfpga_respond_LogError, //101 + fpgad_xfpga_respond_LogError, //102 + fpgad_xfpga_respond_LogError, //103 + fpgad_xfpga_respond_LogError, //104 + + NULL +}; + +STATIC void *fpgad_xfpga_fme_response_contexts[] = { + &fpgad_xfpga_Error_contexts[51], + &fpgad_xfpga_Error_contexts[52], + &fpgad_xfpga_Error_contexts[53], + &fpgad_xfpga_Error_contexts[54], + &fpgad_xfpga_Error_contexts[55], + &fpgad_xfpga_Error_contexts[56], + &fpgad_xfpga_Error_contexts[57], + &fpgad_xfpga_Error_contexts[58], + &fpgad_xfpga_Error_contexts[59], + + &fpgad_xfpga_Error_contexts[60], + &fpgad_xfpga_Error_contexts[61], + &fpgad_xfpga_Error_contexts[62], + &fpgad_xfpga_Error_contexts[63], + &fpgad_xfpga_Error_contexts[64], + &fpgad_xfpga_Error_contexts[65], + &fpgad_xfpga_Error_contexts[66], + &fpgad_xfpga_Error_contexts[67], + &fpgad_xfpga_Error_contexts[68], + &fpgad_xfpga_Error_contexts[69], + &fpgad_xfpga_Error_contexts[70], + &fpgad_xfpga_Error_contexts[71], + + &fpgad_xfpga_Error_contexts[72], + &fpgad_xfpga_Error_contexts[73], + &fpgad_xfpga_Error_contexts[74], + &fpgad_xfpga_Error_contexts[75], + &fpgad_xfpga_Error_contexts[76], + &fpgad_xfpga_Error_contexts[77], + &fpgad_xfpga_Error_contexts[78], + &fpgad_xfpga_Error_contexts[79], + &fpgad_xfpga_Error_contexts[80], + &fpgad_xfpga_Error_contexts[81], + + &fpgad_xfpga_Error_contexts[82], + &fpgad_xfpga_Error_contexts[83], + &fpgad_xfpga_Error_contexts[84], + &fpgad_xfpga_Error_contexts[85], + &fpgad_xfpga_Error_contexts[86], + &fpgad_xfpga_Error_contexts[87], + &fpgad_xfpga_Error_contexts[88], + &fpgad_xfpga_Error_contexts[89], + &fpgad_xfpga_Error_contexts[90], + &fpgad_xfpga_Error_contexts[91], + &fpgad_xfpga_Error_contexts[92], + + &fpgad_xfpga_Error_contexts[93], + &fpgad_xfpga_Error_contexts[94], + &fpgad_xfpga_Error_contexts[95], + &fpgad_xfpga_Error_contexts[96], + &fpgad_xfpga_Error_contexts[97], + &fpgad_xfpga_Error_contexts[98], + &fpgad_xfpga_Error_contexts[99], + &fpgad_xfpga_Error_contexts[100], + &fpgad_xfpga_Error_contexts[101], + &fpgad_xfpga_Error_contexts[102], + &fpgad_xfpga_Error_contexts[103], + &fpgad_xfpga_Error_contexts[104], + + NULL +}; + +int fpgad_plugin_configure(fpgad_monitored_device *d, + const char *cfg) +{ + UNUSED_PARAM(cfg); + + LOG("monitoring vid=0x%04x did=0x%04x objid=0x%x (%s)\n", + d->supported->vendor_id, + d->supported->device_id, + d->object_id, + d->object_type == FPGA_ACCELERATOR ? + "accelerator" : "device"); + + d->type = FPGAD_PLUGIN_TYPE_CALLBACK; + + if (d->object_type == FPGA_ACCELERATOR) { + d->detections = fpgad_xfpga_port_detections; + d->detection_contexts = fpgad_xfpga_port_detection_contexts; + d->responses = fpgad_xfpga_port_responses; + d->response_contexts = fpgad_xfpga_port_response_contexts; + } else { + d->detections = fpgad_xfpga_fme_detections; + d->detection_contexts = fpgad_xfpga_fme_detection_contexts; + d->responses = fpgad_xfpga_fme_responses; + d->response_contexts = fpgad_xfpga_fme_response_contexts; + } + + return 0; +} + +void fpgad_plugin_destroy(fpgad_monitored_device *d) +{ + LOG("stop monitoring vid=0x%04x did=0x%04x objid=0x%x (%s)\n", + d->supported->vendor_id, + d->supported->device_id, + d->object_id, + d->object_type == FPGA_ACCELERATOR ? + "accelerator" : "device"); +}