|
Packit |
534379 |
// Copyright(c) 2018-2020, Intel Corporation
|
|
Packit |
534379 |
//
|
|
Packit |
534379 |
// Redistribution and use in source and binary forms, with or without
|
|
Packit |
534379 |
// modification, are permitted provided that the following conditions are met:
|
|
Packit |
534379 |
//
|
|
Packit |
534379 |
// * Redistributions of source code must retain the above copyright notice,
|
|
Packit |
534379 |
// this list of conditions and the following disclaimer.
|
|
Packit |
534379 |
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
Packit |
534379 |
// this list of conditions and the following disclaimer in the documentation
|
|
Packit |
534379 |
// and/or other materials provided with the distribution.
|
|
Packit |
534379 |
// * Neither the name of Intel Corporation nor the names of its contributors
|
|
Packit |
534379 |
// may be used to endorse or promote products derived from this software
|
|
Packit |
534379 |
// without specific prior written permission.
|
|
Packit |
534379 |
//
|
|
Packit |
534379 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
Packit |
534379 |
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
Packit |
534379 |
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
534379 |
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
Packit |
534379 |
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
Packit |
534379 |
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
Packit |
534379 |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
534379 |
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
Packit |
534379 |
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
534379 |
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
Packit |
534379 |
// POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#ifdef HAVE_CONFIG_H
|
|
Packit |
534379 |
#include <config.h>
|
|
Packit |
534379 |
#endif // HAVE_CONFIG_H
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#include <sys/stat.h>
|
|
Packit |
534379 |
#include <unistd.h>
|
|
Packit |
534379 |
#include <dirent.h>
|
|
Packit |
534379 |
#include <stdio.h>
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#include "common_int.h"
|
|
Packit |
534379 |
#include "opae/error.h"
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#include "error_int.h"
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#define INJECT_ERROR "inject_error"
|
|
Packit |
534379 |
|
|
Packit |
534379 |
fpga_result __XFPGA_API__ xfpga_fpgaReadError(fpga_token token, uint32_t error_num, uint64_t *value)
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
struct _fpga_token *_token = (struct _fpga_token *)token;
|
|
Packit |
534379 |
struct stat st;
|
|
Packit |
534379 |
uint32_t i = 0;
|
|
Packit |
534379 |
fpga_result res = FPGA_OK;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
ASSERT_NOT_NULL(token);
|
|
Packit |
534379 |
if (_token->magic != FPGA_TOKEN_MAGIC) {
|
|
Packit |
534379 |
OPAE_MSG("Invalid token");
|
|
Packit |
534379 |
return FPGA_INVALID_PARAM;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
struct error_list *p = _token->errors;
|
|
Packit |
534379 |
while (p) {
|
|
Packit |
534379 |
if (i == error_num) {
|
|
Packit |
534379 |
// test if file exists
|
|
Packit |
534379 |
if (stat(p->error_file, &st) == -1) {
|
|
Packit |
534379 |
OPAE_MSG("can't stat %s", p->error_file);
|
|
Packit |
534379 |
return FPGA_EXCEPTION;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
res = sysfs_read_u64(p->error_file, value);
|
|
Packit |
534379 |
if (res != FPGA_OK) {
|
|
Packit |
534379 |
OPAE_MSG("can't read error file '%s'", p->error_file);
|
|
Packit |
534379 |
return res;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
return FPGA_OK;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
i++;
|
|
Packit |
534379 |
p = p->next;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
OPAE_MSG("error %d not found", error_num);
|
|
Packit |
534379 |
return FPGA_NOT_FOUND;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
fpga_result __XFPGA_API__
|
|
Packit |
534379 |
xfpga_fpgaClearError(fpga_token token, uint32_t error_num)
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
struct _fpga_token *_token = (struct _fpga_token *)token;
|
|
Packit |
534379 |
struct stat st;
|
|
Packit |
534379 |
uint32_t i = 0;
|
|
Packit |
534379 |
uint64_t value = 0;
|
|
Packit |
534379 |
fpga_result res = FPGA_OK;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
ASSERT_NOT_NULL(token);
|
|
Packit |
534379 |
if (_token->magic != FPGA_TOKEN_MAGIC) {
|
|
Packit |
534379 |
OPAE_MSG("Invalid token");
|
|
Packit |
534379 |
return FPGA_INVALID_PARAM;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
struct error_list *p = _token->errors;
|
|
Packit |
534379 |
while (p) {
|
|
Packit |
534379 |
if (i == error_num) {
|
|
Packit |
534379 |
if (!p->info.can_clear) {
|
|
Packit |
534379 |
OPAE_MSG("can't clear error '%s'", p->info.name);
|
|
Packit |
534379 |
return FPGA_NOT_SUPPORTED;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if (strcmp(p->info.name, INJECT_ERROR) == 0) {
|
|
Packit |
534379 |
value = 0;
|
|
Packit |
534379 |
} else {
|
|
Packit |
534379 |
// read current error value
|
|
Packit |
534379 |
res = xfpga_fpgaReadError(token, error_num, &value);
|
|
Packit |
534379 |
if (res != FPGA_OK)
|
|
Packit |
534379 |
return res;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// write to 'clear' file
|
|
Packit |
534379 |
if (stat(p->clear_file, &st) == -1) {
|
|
Packit |
534379 |
OPAE_MSG("can't stat %s", p->clear_file);
|
|
Packit |
534379 |
return FPGA_EXCEPTION;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
res = sysfs_write_u64(p->clear_file, value);
|
|
Packit |
534379 |
if (res != FPGA_OK) {
|
|
Packit |
534379 |
OPAE_MSG("can't write clear file '%s'", p->clear_file);
|
|
Packit |
534379 |
return res;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
return FPGA_OK;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
i++;
|
|
Packit |
534379 |
p = p->next;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
OPAE_MSG("error info %d not found", error_num);
|
|
Packit |
534379 |
return FPGA_NOT_FOUND;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
fpga_result __XFPGA_API__ xfpga_fpgaClearAllErrors(fpga_token token)
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
struct _fpga_token *_token = (struct _fpga_token *)token;
|
|
Packit |
534379 |
uint32_t i = 0;
|
|
Packit |
534379 |
fpga_result res = FPGA_OK;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
ASSERT_NOT_NULL(token);
|
|
Packit |
534379 |
if (_token->magic != FPGA_TOKEN_MAGIC) {
|
|
Packit |
534379 |
OPAE_MSG("Invalid token");
|
|
Packit |
534379 |
return FPGA_INVALID_PARAM;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
struct error_list *p = _token->errors;
|
|
Packit |
534379 |
while (p) {
|
|
Packit |
534379 |
// if error can be cleared
|
|
Packit |
534379 |
if (p->info.can_clear) {
|
|
Packit |
534379 |
// clear error
|
|
Packit |
534379 |
res = xfpga_fpgaClearError(token, i);
|
|
Packit |
534379 |
if (res != FPGA_OK)
|
|
Packit |
534379 |
return res;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
i++;
|
|
Packit |
534379 |
p = p->next;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
return FPGA_OK;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
fpga_result __XFPGA_API__ xfpga_fpgaGetErrorInfo(fpga_token token,
|
|
Packit |
534379 |
uint32_t error_num,
|
|
Packit |
534379 |
struct fpga_error_info *error_info)
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
struct _fpga_token *_token = (struct _fpga_token *)token;
|
|
Packit |
534379 |
uint32_t i = 0;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if (!error_info) {
|
|
Packit |
534379 |
OPAE_MSG("error_info is NULL");
|
|
Packit |
534379 |
return FPGA_INVALID_PARAM;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
ASSERT_NOT_NULL(token);
|
|
Packit |
534379 |
if (_token->magic != FPGA_TOKEN_MAGIC) {
|
|
Packit |
534379 |
OPAE_MSG("Invalid token");
|
|
Packit |
534379 |
return FPGA_INVALID_PARAM;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
struct error_list *p = _token->errors;
|
|
Packit |
534379 |
while (p) {
|
|
Packit |
534379 |
if (i == error_num) {
|
|
Packit |
534379 |
memcpy(error_info, &p->info, sizeof(struct fpga_error_info));
|
|
Packit |
534379 |
return FPGA_OK;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
i++;
|
|
Packit |
534379 |
p = p->next;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
OPAE_MSG("error info %d not found", error_num);
|
|
Packit |
534379 |
return FPGA_NOT_FOUND;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
/* files and directories to ignore when looking for errors */
|
|
Packit |
534379 |
#define NUM_ERRORS_EXCLUDE 4
|
|
Packit |
534379 |
const char *errors_exclude[NUM_ERRORS_EXCLUDE] = {
|
|
Packit |
534379 |
"revision",
|
|
Packit |
534379 |
"uevent",
|
|
Packit |
534379 |
"power",
|
|
Packit |
534379 |
"clear"
|
|
Packit |
534379 |
};
|
|
Packit |
534379 |
|
|
Packit |
534379 |
/* files that can be cleared by writing their value to them */
|
|
Packit |
534379 |
#define NUM_ERRORS_CLEARABLE 6
|
|
Packit |
534379 |
const char *errors_clearable[] = {
|
|
Packit |
534379 |
"pcie0_errors",
|
|
Packit |
534379 |
"pcie1_errors",
|
|
Packit |
534379 |
"warning_errors",
|
|
Packit |
534379 |
"inject_error",
|
|
Packit |
534379 |
"fme_errors",
|
|
Packit |
534379 |
"errors"
|
|
Packit |
534379 |
};
|
|
Packit |
534379 |
|
|
Packit |
534379 |
/* Walks the given directory and adds error entries to `list`.
|
|
Packit |
534379 |
* This function is called during enumeration when adding tokens to
|
|
Packit |
534379 |
* the global tokens list. When tokens are cloned, their error
|
|
Packit |
534379 |
* lists are only shallowly copied (which works because errors of
|
|
Packit |
534379 |
* a token never change).
|
|
Packit |
534379 |
* Note that build_error_list() does not check for dupliates; if
|
|
Packit |
534379 |
* called again on the same list, it will add all found errors again.
|
|
Packit |
534379 |
* Returns the number of error entries added to `list` */
|
|
Packit |
534379 |
uint32_t
|
|
Packit |
534379 |
build_error_list(const char *path, struct error_list **list)
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
struct dirent *de;
|
|
Packit |
534379 |
DIR *dir;
|
|
Packit |
534379 |
struct stat st;
|
|
Packit |
534379 |
char basedir[FILENAME_MAX] = { 0, };
|
|
Packit |
534379 |
int len;
|
|
Packit |
534379 |
int subpath_len = 0;
|
|
Packit |
534379 |
uint32_t n = 0;
|
|
Packit |
534379 |
unsigned int i;
|
|
Packit |
534379 |
struct error_list **el = list;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
len = strnlen(path, FILENAME_MAX - 1);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// add 3 to the len
|
|
Packit |
534379 |
// 1 for the '/' char
|
|
Packit |
534379 |
// 1 for the minimum length of a file appended
|
|
Packit |
534379 |
// 1 for null string to terminate
|
|
Packit |
534379 |
// if we go over now, then leave without doing anything else
|
|
Packit |
534379 |
if (len+3 > FILENAME_MAX) {
|
|
Packit |
534379 |
OPAE_MSG("path too long");
|
|
Packit |
534379 |
return 0;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
len = snprintf(basedir, sizeof(basedir),
|
|
Packit |
534379 |
"%s/", path);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// now we've added one to length
|
|
Packit |
534379 |
|
|
Packit |
534379 |
dir = opendir(path);
|
|
Packit |
534379 |
if (!dir) {
|
|
Packit |
534379 |
OPAE_MSG("unable to open %s", path);
|
|
Packit |
534379 |
return 0;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
while ((de = readdir(dir))) {
|
|
Packit |
534379 |
size_t blen;
|
|
Packit |
534379 |
size_t dlen;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// skip hidden ('.*') files (includes "." and "..")
|
|
Packit |
534379 |
if (de->d_name[0] == '.')
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// skip names on blacklist
|
|
Packit |
534379 |
for (i = 0; i < NUM_ERRORS_EXCLUDE; i++) {
|
|
Packit |
534379 |
if (strcmp(de->d_name, errors_exclude[i]) == 0) {
|
|
Packit |
534379 |
break;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
if (i < NUM_ERRORS_EXCLUDE)
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
subpath_len = strnlen(de->d_name, sizeof(de->d_name) - 1);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// check if the result abs path is longer than our max
|
|
Packit |
534379 |
if (len + subpath_len > FILENAME_MAX) {
|
|
Packit |
534379 |
OPAE_MSG("Error path length is too long");
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// build absolute path
|
|
Packit |
534379 |
// dmax (arg2) is restricted max length of resulting dest,
|
|
Packit |
534379 |
// including null - it must also be at least smax+1 (arg4)
|
|
Packit |
534379 |
strncpy(basedir + len, de->d_name, subpath_len + 1);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// try accessing file/dir
|
|
Packit |
534379 |
if (lstat(basedir, &st) == -1) {
|
|
Packit |
534379 |
OPAE_MSG("can't stat %s", basedir);
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// skip symlinks
|
|
Packit |
534379 |
if (S_ISLNK(st.st_mode))
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// recursively dive into subdirectories
|
|
Packit |
534379 |
if (S_ISDIR(st.st_mode)) {
|
|
Packit |
534379 |
n += build_error_list(basedir, el);
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// not blacklisted, not hidden, accessible, no symlink, no dir -> count and append it!
|
|
Packit |
534379 |
n++;
|
|
Packit |
534379 |
if (!el) // no list
|
|
Packit |
534379 |
continue;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// append error info to list
|
|
Packit |
534379 |
struct error_list *new_entry = malloc(sizeof(struct error_list));
|
|
Packit |
534379 |
if (!new_entry) {
|
|
Packit |
534379 |
OPAE_MSG("can't allocate memory");
|
|
Packit |
534379 |
n--;
|
|
Packit |
534379 |
break;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
dlen = strnlen(de->d_name, sizeof(new_entry->info.name) - 1);
|
|
Packit |
534379 |
memcpy(new_entry->info.name, de->d_name, dlen);
|
|
Packit |
534379 |
new_entry->info.name[dlen] = '\0';
|
|
Packit |
534379 |
|
|
Packit |
534379 |
blen = strnlen(basedir, sizeof(new_entry->error_file) - 1);
|
|
Packit |
534379 |
memcpy(new_entry->error_file, basedir, blen);
|
|
Packit |
534379 |
new_entry->error_file[blen] = '\0';
|
|
Packit |
534379 |
|
|
Packit |
534379 |
new_entry->next = NULL;
|
|
Packit |
534379 |
// Errors can be cleared:
|
|
Packit |
534379 |
// * if the name is "errors" and there is a file called "clear" (generic case), OR
|
|
Packit |
534379 |
// * if the name is in the "errors_clearable" table
|
|
Packit |
534379 |
new_entry->info.can_clear = false;
|
|
Packit |
534379 |
if (strcmp(de->d_name, "errors") == 0 &&
|
|
Packit |
534379 |
!stat(FPGA_SYSFS_CLASS_PATH_INTEL, &st)) {
|
|
Packit |
534379 |
strncpy(basedir + len, "clear", 6);
|
|
Packit |
534379 |
// try accessing clear file
|
|
Packit |
534379 |
if (lstat(basedir, &st) != -1) {
|
|
Packit |
534379 |
new_entry->info.can_clear = true;
|
|
Packit |
534379 |
memcpy(new_entry->clear_file, basedir, blen);
|
|
Packit |
534379 |
new_entry->clear_file[blen] = '\0';
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
} else {
|
|
Packit |
534379 |
for (i = 0; i < NUM_ERRORS_CLEARABLE; i++) {
|
|
Packit |
534379 |
if (strcmp(de->d_name, errors_clearable[i]) == 0) {
|
|
Packit |
534379 |
memcpy(basedir + len, de->d_name, dlen);
|
|
Packit |
534379 |
*(basedir + len + dlen) = '\0';
|
|
Packit |
534379 |
// try accessing clear file
|
|
Packit |
534379 |
if (lstat(basedir, &st) != -1) {
|
|
Packit |
534379 |
new_entry->info.can_clear = true;
|
|
Packit |
534379 |
memcpy(new_entry->clear_file, basedir, blen);
|
|
Packit |
534379 |
new_entry->clear_file[blen] = '\0';
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if (new_entry && !new_entry->info.can_clear) {
|
|
Packit |
534379 |
memset(new_entry->clear_file, 0, sizeof(new_entry->clear_file));
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// find end of list
|
|
Packit |
534379 |
while (*el)
|
|
Packit |
534379 |
el = &(*el)->next;
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// append
|
|
Packit |
534379 |
if (new_entry)
|
|
Packit |
534379 |
*el = new_entry;
|
|
Packit |
534379 |
el = &new_entry->next;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
closedir(dir);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
return n;
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
uint32_t count_error_files(const char *path)
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
return build_error_list(path, NULL);
|
|
Packit |
534379 |
}
|