mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
fe986f9ef0
Add a more advanced BAR test that writes all BARs in one go, and then reads them back and verifies that the value matches the BAR number bitwise OR'ed with offset, this allows us to verify: - The BAR number was what we intended to read - The offset was what we intended to read This allows us to detect potential address translation issues on the EP. Reading back the BAR directly after writing will not allow us to detect the case where inbound address translation on the endpoint incorrectly causes multiple BARs to be redirected to the same memory region (within the EP). Link: https://lore.kernel.org/r/20241116032045.2574168-2-cassel@kernel.org Signed-off-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
265 lines
6.0 KiB
C
265 lines
6.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/**
|
|
* Userspace PCI Endpoint Test Module
|
|
*
|
|
* Copyright (C) 2017 Texas Instruments
|
|
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
|
|
#include <linux/pcitest.h>
|
|
|
|
static char *result[] = { "NOT OKAY", "OKAY" };
|
|
static char *irq[] = { "LEGACY", "MSI", "MSI-X" };
|
|
|
|
struct pci_test {
|
|
char *device;
|
|
char barnum;
|
|
bool consecutive_bar_test;
|
|
bool legacyirq;
|
|
unsigned int msinum;
|
|
unsigned int msixnum;
|
|
int irqtype;
|
|
bool set_irqtype;
|
|
bool get_irqtype;
|
|
bool clear_irq;
|
|
bool read;
|
|
bool write;
|
|
bool copy;
|
|
unsigned long size;
|
|
bool use_dma;
|
|
};
|
|
|
|
static int run_test(struct pci_test *test)
|
|
{
|
|
struct pci_endpoint_test_xfer_param param = {};
|
|
int ret = -EINVAL;
|
|
int fd;
|
|
|
|
fd = open(test->device, O_RDWR);
|
|
if (fd < 0) {
|
|
perror("can't open PCI Endpoint Test device");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (test->barnum >= 0 && test->barnum <= 5) {
|
|
ret = ioctl(fd, PCITEST_BAR, test->barnum);
|
|
fprintf(stdout, "BAR%d:\t\t", test->barnum);
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->consecutive_bar_test) {
|
|
ret = ioctl(fd, PCITEST_BARS);
|
|
fprintf(stdout, "Consecutive BAR test:\t\t");
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->set_irqtype) {
|
|
ret = ioctl(fd, PCITEST_SET_IRQTYPE, test->irqtype);
|
|
fprintf(stdout, "SET IRQ TYPE TO %s:\t\t", irq[test->irqtype]);
|
|
if (ret < 0)
|
|
fprintf(stdout, "FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->get_irqtype) {
|
|
ret = ioctl(fd, PCITEST_GET_IRQTYPE);
|
|
fprintf(stdout, "GET IRQ TYPE:\t\t");
|
|
if (ret < 0)
|
|
fprintf(stdout, "FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", irq[ret]);
|
|
}
|
|
|
|
if (test->clear_irq) {
|
|
ret = ioctl(fd, PCITEST_CLEAR_IRQ);
|
|
fprintf(stdout, "CLEAR IRQ:\t\t");
|
|
if (ret < 0)
|
|
fprintf(stdout, "FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->legacyirq) {
|
|
ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
|
|
fprintf(stdout, "LEGACY IRQ:\t");
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->msinum > 0 && test->msinum <= 32) {
|
|
ret = ioctl(fd, PCITEST_MSI, test->msinum);
|
|
fprintf(stdout, "MSI%u:\t\t", test->msinum);
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->msixnum > 0 && test->msixnum <= 2048) {
|
|
ret = ioctl(fd, PCITEST_MSIX, test->msixnum);
|
|
fprintf(stdout, "MSI-X%u:\t\t", test->msixnum);
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->write) {
|
|
param.size = test->size;
|
|
if (test->use_dma)
|
|
param.flags = PCITEST_FLAGS_USE_DMA;
|
|
ret = ioctl(fd, PCITEST_WRITE, ¶m);
|
|
fprintf(stdout, "WRITE (%7lu bytes):\t\t", test->size);
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->read) {
|
|
param.size = test->size;
|
|
if (test->use_dma)
|
|
param.flags = PCITEST_FLAGS_USE_DMA;
|
|
ret = ioctl(fd, PCITEST_READ, ¶m);
|
|
fprintf(stdout, "READ (%7lu bytes):\t\t", test->size);
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
if (test->copy) {
|
|
param.size = test->size;
|
|
if (test->use_dma)
|
|
param.flags = PCITEST_FLAGS_USE_DMA;
|
|
ret = ioctl(fd, PCITEST_COPY, ¶m);
|
|
fprintf(stdout, "COPY (%7lu bytes):\t\t", test->size);
|
|
if (ret < 0)
|
|
fprintf(stdout, "TEST FAILED\n");
|
|
else
|
|
fprintf(stdout, "%s\n", result[ret]);
|
|
}
|
|
|
|
fflush(stdout);
|
|
close(fd);
|
|
return (ret < 0) ? ret : 1 - ret; /* return 0 if test succeeded */
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int c;
|
|
struct pci_test *test;
|
|
|
|
test = calloc(1, sizeof(*test));
|
|
if (!test) {
|
|
perror("Fail to allocate memory for pci_test\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* since '0' is a valid BAR number, initialize it to -1 */
|
|
test->barnum = -1;
|
|
|
|
/* set default size as 100KB */
|
|
test->size = 0x19000;
|
|
|
|
/* set default endpoint device */
|
|
test->device = "/dev/pci-endpoint-test.0";
|
|
|
|
while ((c = getopt(argc, argv, "D:b:Cm:x:i:deIlhrwcs:")) != EOF)
|
|
switch (c) {
|
|
case 'D':
|
|
test->device = optarg;
|
|
continue;
|
|
case 'b':
|
|
test->barnum = atoi(optarg);
|
|
if (test->barnum < 0 || test->barnum > 5)
|
|
goto usage;
|
|
continue;
|
|
case 'C':
|
|
test->consecutive_bar_test = true;
|
|
continue;
|
|
case 'l':
|
|
test->legacyirq = true;
|
|
continue;
|
|
case 'm':
|
|
test->msinum = atoi(optarg);
|
|
if (test->msinum < 1 || test->msinum > 32)
|
|
goto usage;
|
|
continue;
|
|
case 'x':
|
|
test->msixnum = atoi(optarg);
|
|
if (test->msixnum < 1 || test->msixnum > 2048)
|
|
goto usage;
|
|
continue;
|
|
case 'i':
|
|
test->irqtype = atoi(optarg);
|
|
if (test->irqtype < 0 || test->irqtype > 2)
|
|
goto usage;
|
|
test->set_irqtype = true;
|
|
continue;
|
|
case 'I':
|
|
test->get_irqtype = true;
|
|
continue;
|
|
case 'r':
|
|
test->read = true;
|
|
continue;
|
|
case 'w':
|
|
test->write = true;
|
|
continue;
|
|
case 'c':
|
|
test->copy = true;
|
|
continue;
|
|
case 'e':
|
|
test->clear_irq = true;
|
|
continue;
|
|
case 's':
|
|
test->size = strtoul(optarg, NULL, 0);
|
|
continue;
|
|
case 'd':
|
|
test->use_dma = true;
|
|
continue;
|
|
case 'h':
|
|
default:
|
|
usage:
|
|
fprintf(stderr,
|
|
"usage: %s [options]\n"
|
|
"Options:\n"
|
|
"\t-D <dev> PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n"
|
|
"\t-b <bar num> BAR test (bar number between 0..5)\n"
|
|
"\t-C Consecutive BAR test\n"
|
|
"\t-m <msi num> MSI test (msi number between 1..32)\n"
|
|
"\t-x <msix num> \tMSI-X test (msix number between 1..2048)\n"
|
|
"\t-i <irq type> \tSet IRQ type (0 - Legacy, 1 - MSI, 2 - MSI-X)\n"
|
|
"\t-e Clear IRQ\n"
|
|
"\t-I Get current IRQ type configured\n"
|
|
"\t-d Use DMA\n"
|
|
"\t-l Legacy IRQ test\n"
|
|
"\t-r Read buffer test\n"
|
|
"\t-w Write buffer test\n"
|
|
"\t-c Copy buffer test\n"
|
|
"\t-s <size> Size of buffer {default: 100KB}\n"
|
|
"\t-h Print this help message\n",
|
|
argv[0]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return run_test(test);
|
|
}
|