lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260118135440.1958279-38-den@valinux.co.jp>
Date: Sun, 18 Jan 2026 22:54:39 +0900
From: Koichiro Den <den@...inux.co.jp>
To: Frank.Li@....com,
	dave.jiang@...el.com,
	cassel@...nel.org,
	mani@...nel.org,
	kwilczynski@...nel.org,
	kishon@...nel.org,
	bhelgaas@...gle.com,
	geert+renesas@...der.be,
	robh@...nel.org,
	vkoul@...nel.org,
	jdmason@...zu.us,
	allenbh@...il.com,
	jingoohan1@...il.com,
	lpieralisi@...nel.org
Cc: linux-pci@...r.kernel.org,
	linux-doc@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	linux-renesas-soc@...r.kernel.org,
	devicetree@...r.kernel.org,
	dmaengine@...r.kernel.org,
	iommu@...ts.linux.dev,
	ntb@...ts.linux.dev,
	netdev@...r.kernel.org,
	linux-kselftest@...r.kernel.org,
	arnd@...db.de,
	gregkh@...uxfoundation.org,
	joro@...tes.org,
	will@...nel.org,
	robin.murphy@....com,
	magnus.damm@...il.com,
	krzk+dt@...nel.org,
	conor+dt@...nel.org,
	corbet@....net,
	skhan@...uxfoundation.org,
	andriy.shevchenko@...ux.intel.com,
	jbrunet@...libre.com,
	utkarsh02t@...il.com
Subject: [RFC PATCH v4 37/38] misc: pci_endpoint_test: Add remote eDMA transfer test mode

Add a new test mode controlled by a flag, PCITEST_FLAGS_USE_REMOTE_EDMA.
When requested, the driver:
- Issues COMMAND_REMOTE_EDMA_SETUP to the endpoint and locates the BAR
  containing a pcitest_edma_info header (magic/version).
- Creates a remote dw-edma instance by ioremapping the endpoint's
  exposed eDMA registers and linked-list regions and probing dw-edma on
  top of it.
- Requests one DMA_SLAVE channel per direction and performs the
  transfer.
- Uses COMMAND_REMOTE_EDMA_CHECKSUM to validate the result when the
  transfer direction is host-to-endpoint. For the opposite direction,
  the endpoint provides the expected checksum up front.

One MSI/MSI-X vector is reserved for the remote dw-edma instance by
freeing the last test IRQ vector. This keeps existing MSI/MSI-X tests
unchanged unless the remote-eDMA mode is invoked.

BAR read/write tests skip the BAR reserved for remote-eDMA metadata to
avoid corrupting the eDMA window.

Signed-off-by: Koichiro Den <den@...inux.co.jp>
---
 drivers/misc/pci_endpoint_test.c | 633 +++++++++++++++++++++++++++++++
 include/uapi/linux/pcitest.h     |   3 +-
 2 files changed, 635 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 1c0fd185114f..52d700374ac6 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -8,7 +8,10 @@
 
 #include <linux/crc32.h>
 #include <linux/cleanup.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/fs.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
@@ -17,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/random.h>
+#include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/pci.h>
@@ -39,6 +43,8 @@
 #define COMMAND_COPY				BIT(5)
 #define COMMAND_ENABLE_DOORBELL			BIT(6)
 #define COMMAND_DISABLE_DOORBELL		BIT(7)
+#define COMMAND_REMOTE_EDMA_SETUP		BIT(8)
+#define COMMAND_REMOTE_EDMA_CHECKSUM		BIT(9)
 
 #define PCI_ENDPOINT_TEST_STATUS		0x8
 #define STATUS_READ_SUCCESS			BIT(0)
@@ -55,6 +61,10 @@
 #define STATUS_DOORBELL_ENABLE_FAIL		BIT(11)
 #define STATUS_DOORBELL_DISABLE_SUCCESS		BIT(12)
 #define STATUS_DOORBELL_DISABLE_FAIL		BIT(13)
+#define STATUS_REMOTE_EDMA_SETUP_SUCCESS	BIT(14)
+#define STATUS_REMOTE_EDMA_SETUP_FAIL		BIT(15)
+#define STATUS_REMOTE_EDMA_CHECKSUM_SUCCESS	BIT(16)
+#define STATUS_REMOTE_EDMA_CHECKSUM_FAIL	BIT(17)
 
 #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR	0x0c
 #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR	0x10
@@ -130,6 +140,9 @@ struct pci_endpoint_test {
 	size_t alignment;
 	u32 ep_caps;
 	const char *name;
+
+	/* For extended tests that rely on vendor-specific features */
+	void *data;
 };
 
 struct pci_endpoint_test_data {
@@ -149,6 +162,610 @@ static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test,
 	writel(value, test->base + offset);
 }
 
+static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id);
+
+#if IS_REACHABLE(CONFIG_DW_EDMA)
+#include <linux/dma/edma.h>
+
+#define PCITEST_EDMA_INFO_MAGIC		0x414d4445U /* 'EDMA' */
+#define PCITEST_EDMA_INFO_VERSION	0x00010000U
+
+struct pci_endpoint_test_edma {
+	bool			probed;
+	void __iomem		*bar_base;
+	int			irq;
+
+	/* Remote dw-edma instance */
+	struct dw_edma_chip	chip;
+
+	/* One channel per direction */
+	struct dma_chan		*m2d;
+	struct dma_chan		*d2m;
+};
+
+struct pcitest_edma_info {
+	__le32 magic;
+	__le32 version;
+
+	__le32 reg_off;
+	__le32 reg_size;
+
+	__le64 ll_rd_phys;
+	__le32 ll_rd_off;
+	__le32 ll_rd_size;
+
+	__le64 ll_wr_phys;
+	__le32 ll_wr_off;
+	__le32 ll_wr_size;
+
+	__le64 test_buf_phys;
+	__le32 test_buf_size;
+};
+
+struct pci_endpoint_test_edma_filter {
+	struct device *dma_dev;
+	unsigned long direction;
+};
+
+static bool test_edma_filter_fn(struct dma_chan *chan, void *param)
+{
+	struct pci_endpoint_test_edma_filter *filter = param;
+	u32 dir = filter->direction;
+	struct dma_slave_caps caps;
+	int ret;
+
+	if (chan->device->dev != filter->dma_dev)
+		return false;
+
+	ret = dma_get_slave_caps(chan, &caps);
+	if (ret < 0)
+		return false;
+
+	return !!(caps.directions & dir);
+}
+
+static int pci_endpoint_test_edma_irq_vector(struct device *dev, unsigned int nr)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct pci_endpoint_test *test = pci_get_drvdata(pdev);
+	struct pci_endpoint_test_edma *edma;
+
+	if (!test)
+		return -EINVAL;
+
+	edma = test->data;
+	if (!edma)
+		return -EINVAL;
+
+	/*
+	 * Only one vector is reserved for remote eDMA use, thus 'nr' is
+	 * ignored. See pci_endpoint_test_edma_reserve_irq().
+	 */
+	return pci_irq_vector(pdev, edma->irq);
+}
+
+static enum pci_barno pci_endpoint_test_edma_bar(struct pci_dev *pdev)
+{
+	int bar;
+
+	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
+		void __iomem *base;
+		u32 magic;
+
+		if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
+			continue;
+		if (!pci_resource_len(pdev, bar))
+			continue;
+
+		base = pci_iomap_range(pdev, bar, 0, sizeof(u32));
+		if (!base)
+			continue;
+
+		magic = ioread32(base);
+		pci_iounmap(pdev, base);
+
+		if (magic == PCITEST_EDMA_INFO_MAGIC)
+			return bar;
+	}
+	return NO_BAR;
+}
+
+static bool pci_endpoint_test_bar_is_reserved(struct pci_endpoint_test *test,
+					      enum pci_barno barno)
+{
+	struct pci_dev *pdev = test->pdev;
+	enum pci_barno edma_bar = pci_endpoint_test_edma_bar(pdev);
+
+	return barno == NO_BAR || barno == edma_bar;
+}
+
+static void pci_endpoint_test_dw_edma_cleanup(struct pci_endpoint_test *test,
+					      struct pci_endpoint_test_edma *edma)
+{
+	if (!edma)
+		return;
+
+	if (edma->m2d) {
+		dmaengine_terminate_sync(edma->m2d);
+		dma_release_channel(edma->m2d);
+		edma->m2d = NULL;
+	}
+
+	if (edma->d2m) {
+		dmaengine_terminate_sync(edma->d2m);
+		dma_release_channel(edma->d2m);
+		edma->d2m = NULL;
+	}
+
+	if (edma->probed) {
+		dw_edma_remove(&edma->chip);
+		edma->probed = false;
+	}
+
+	if (edma->bar_base) {
+		pci_iounmap(test->pdev, edma->bar_base);
+		edma->bar_base = NULL;
+	}
+}
+
+static void pci_endpoint_test_remote_edma_teardown(struct pci_endpoint_test *test)
+{
+	struct pci_endpoint_test_edma *edma = test->data;
+
+	pci_endpoint_test_dw_edma_cleanup(test, edma);
+	kfree(edma);
+	test->data = NULL;
+}
+
+/*
+ * Reserve exactly one IRQ vector for dw-edma by freeing the last handler.
+ * This avoids changing existing MSI/MSI-X tests unless remote eDMA is used.
+ */
+static int pci_endpoint_test_edma_reserve_irq(struct pci_endpoint_test *test)
+{
+	struct pci_dev *pdev = test->pdev;
+
+	if (test->irq_type != PCITEST_IRQ_TYPE_MSI &&
+	    test->irq_type != PCITEST_IRQ_TYPE_MSIX)
+		return -EOPNOTSUPP;
+
+	if (test->num_irqs < 2)
+		return -ENOSPC;
+
+	/* use the last vector for remote eDMA use */
+	free_irq(pci_irq_vector(pdev, test->num_irqs - 1), test);
+	return test->num_irqs - 1;
+}
+
+static void pci_endpoint_test_edma_restore_irq(struct pci_endpoint_test *test)
+{
+	struct pci_dev *pdev = test->pdev;
+	int ret;
+
+	ret = request_irq(pci_irq_vector(pdev, test->num_irqs - 1),
+			  pci_endpoint_test_irqhandler, IRQF_SHARED, test->name,
+			  test);
+	if (ret)
+		dev_warn(&pdev->dev,
+			 "failed to restore IRQ vector %d after remote eDMA: %d\n",
+			 test->num_irqs - 1, ret);
+}
+
+static const struct dw_edma_plat_ops test_edma_ops = {
+	.irq_vector     = pci_endpoint_test_edma_irq_vector,
+};
+
+static int pci_endpoint_test_dw_edma_setup(struct pci_endpoint_test *test)
+{
+	struct pci_endpoint_test_edma *edma = test->data;
+	struct pci_endpoint_test_edma_filter f;
+	struct pci_endpoint_test_edma *new;
+	struct pci_dev *pdev = test->pdev;
+	struct device *dev = &pdev->dev;
+	struct pcitest_edma_info info;
+	resource_size_t bar_size;
+	u32 ll_rd_off, ll_rd_size;
+	u32 ll_wr_off, ll_wr_size;
+	u32 reg_off, reg_size;
+	dma_cap_mask_t mask;
+	enum pci_barno bar;
+	int ret;
+
+	if (edma && edma->probed)
+		return 0;
+
+	new = kzalloc_obj(*new, GFP_KERNEL);
+	if (!new)
+		return -ENOMEM;
+
+	ret = pci_endpoint_test_edma_reserve_irq(test);
+	if (ret < 0)
+		goto err_free;
+	new->irq = ret;
+
+	bar = pci_endpoint_test_edma_bar(pdev);
+	if (bar == NO_BAR) {
+		ret = -EOPNOTSUPP;
+		goto err_restore_irq;
+	}
+
+	new->bar_base = pci_iomap(pdev, bar, 0);
+	if (!new->bar_base) {
+		ret = -ENOMEM;
+		goto err_restore_irq;
+	}
+	bar_size = pci_resource_len(pdev, bar);
+
+	/* Snapshot the info (avoid repeated __iomem reads). */
+	memcpy_fromio(&info, new->bar_base, sizeof(info));
+	if (le32_to_cpu(info.magic) != PCITEST_EDMA_INFO_MAGIC ||
+	    le32_to_cpu(info.version) != PCITEST_EDMA_INFO_VERSION) {
+		dev_err(&pdev->dev, "Invalid eDMA info\n");
+		ret = -EINVAL;
+		goto err_cleanup;
+	}
+
+	reg_off = le32_to_cpu(info.reg_off);
+	reg_size = le32_to_cpu(info.reg_size);
+	ll_rd_off = le32_to_cpu(info.ll_rd_off);
+	ll_rd_size = le32_to_cpu(info.ll_rd_size);
+	ll_wr_off = le32_to_cpu(info.ll_wr_off);
+	ll_wr_size = le32_to_cpu(info.ll_wr_size);
+
+	if (reg_off > bar_size || reg_size > bar_size - reg_off ||
+	    ll_rd_off > bar_size || ll_rd_size > bar_size - ll_rd_off ||
+	    ll_wr_off > bar_size || ll_wr_size > bar_size - ll_wr_off) {
+		dev_err(&pdev->dev, "eDMA info offsets out of BAR range\n");
+		ret = -EINVAL;
+		goto err_cleanup;
+	}
+
+	memset(&new->chip, 0, sizeof(new->chip));
+	new->chip.dev = &pdev->dev;
+	new->chip.mf = EDMA_MF_EDMA_UNROLL;
+	new->chip.nr_irqs = 1;
+	new->chip.ops = &test_edma_ops;
+	new->chip.reg_base = new->bar_base + reg_off;
+	new->chip.ll_rd_cnt = 1;
+	new->chip.ll_region_rd[0].paddr = le64_to_cpu(info.ll_rd_phys);
+	new->chip.ll_region_rd[0].vaddr.io = new->bar_base + ll_rd_off;
+	new->chip.ll_region_rd[0].sz = ll_rd_size;
+	new->chip.ll_wr_cnt = 1;
+	new->chip.ll_region_wr[0].paddr = le64_to_cpu(info.ll_wr_phys);
+	new->chip.ll_region_wr[0].vaddr.io = new->bar_base + ll_wr_off;
+	new->chip.ll_region_wr[0].sz = ll_wr_size;
+
+	test->data = new;
+	ret = dw_edma_probe(&new->chip);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to probe eDMA: %d\n", ret);
+		goto err_cleanup;
+	}
+	new->probed = true;
+
+	/* Request one channel per direction. */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	f.dma_dev = dev;
+	f.direction = BIT(DMA_MEM_TO_DEV);
+	new->m2d = dma_request_channel(mask, test_edma_filter_fn, &f);
+	f.direction = BIT(DMA_DEV_TO_MEM);
+	new->d2m = dma_request_channel(mask, test_edma_filter_fn, &f);
+	if (!new->m2d || !new->d2m) {
+		ret = -ENODEV;
+		goto err_cleanup;
+	}
+
+	/*
+	 * Best-effort attempt, ie. even if it fails for some reason, the
+	 * endpoint will ignore endpoint-local interrupts (edma_int bus).
+	 */
+	dw_edma_chan_irq_config(new->m2d, DW_EDMA_CH_IRQ_REMOTE);
+	dw_edma_chan_irq_config(new->d2m, DW_EDMA_CH_IRQ_REMOTE);
+
+	return 0;
+err_cleanup:
+	pci_endpoint_test_dw_edma_cleanup(test, new);
+err_restore_irq:
+	pci_endpoint_test_edma_restore_irq(test);
+err_free:
+	kfree(new);
+	test->data = NULL;
+	return ret;
+}
+
+static int pci_endpoint_test_remote_edma_setup(struct pci_endpoint_test *test,
+					       size_t size)
+{
+	struct pci_dev *pdev = test->pdev;
+	struct device *dev = &pdev->dev;
+	unsigned long left;
+	u32 status;
+
+	/* Same rule as existing tests: IRQ type must be configured first */
+	if (test->irq_type != PCITEST_IRQ_TYPE_MSI &&
+	    test->irq_type != PCITEST_IRQ_TYPE_MSIX) {
+		dev_err(dev, "Invalid IRQ type for remote eDMA\n");
+		return -EINVAL;
+	}
+
+	/* Need one spare vector for dw-edma */
+	if (test->num_irqs < 2)
+		return -ENOSPC;
+
+	/*
+	 * Ensure EP command handler won't reject us due to stale flags.
+	 * (remote-eDMA setup itself is not "FLAG_USE_DMA")
+	 */
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_FLAGS, 0);
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
+				 test->irq_type);
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+
+	reinit_completion(&test->irq_raised);
+	test->last_irq = -ENODATA;
+
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+				 COMMAND_REMOTE_EDMA_SETUP);
+
+	left = wait_for_completion_timeout(&test->irq_raised,
+					   msecs_to_jiffies(1000));
+	if (!left)
+		return -ETIMEDOUT;
+
+	status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+	if (status & STATUS_REMOTE_EDMA_SETUP_FAIL) {
+		dev_err(dev, "Endpoint failed to setup remote eDMA window\n");
+		return -EIO;
+	}
+	if (!(status & STATUS_REMOTE_EDMA_SETUP_SUCCESS)) {
+		dev_err(dev,
+			"Endpoint did not report remote eDMA setup success\n");
+		return -EIO;
+	}
+
+	return pci_endpoint_test_dw_edma_setup(test);
+}
+
+static int pci_endpoint_test_edma_xfer(struct pci_dev *pdev,
+				       struct pci_endpoint_test_edma *edma,
+				       void *buf, size_t len,
+				       dma_addr_t dev_addr,
+				       enum dma_transfer_direction dir)
+{
+	struct dma_async_tx_descriptor *tx;
+	enum dma_data_direction map_dir;
+	struct device *dev = &pdev->dev;
+	struct dma_slave_config cfg;
+	struct completion done;
+	struct dma_chan *chan;
+	struct scatterlist sg;
+	dma_cookie_t cookie;
+	int ret;
+
+	memset(&cfg, 0, sizeof(cfg));
+	if (dir == DMA_MEM_TO_DEV) {
+		chan = edma->m2d;
+		map_dir = DMA_TO_DEVICE;
+		cfg.direction = DMA_MEM_TO_DEV;
+		cfg.dst_addr = dev_addr;
+	} else if (dir == DMA_DEV_TO_MEM) {
+		chan = edma->d2m;
+		map_dir = DMA_FROM_DEVICE;
+		cfg.direction = DMA_DEV_TO_MEM;
+		cfg.src_addr = dev_addr;
+	} else {
+		return -EINVAL;
+	}
+
+	ret = dmaengine_slave_config(chan, &cfg);
+	if (ret)
+		return ret;
+
+	sg_init_one(&sg, buf, len);
+	if (!dma_map_sg(dev, &sg, 1, map_dir)) {
+		dev_err(dev, "unable to map local address\n");
+		return -EIO;
+	}
+
+	tx = dmaengine_prep_slave_sg(chan, &sg, 1, dir,
+				     DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!tx) {
+		dev_err(dev, "failed to prepare slave for sg\n");
+		ret = -EIO;
+		goto unmap;
+	}
+
+	init_completion(&done);
+	tx->callback = (dma_async_tx_callback)complete;
+	tx->callback_param = &done;
+
+	cookie = dmaengine_submit(tx);
+	ret = dma_submit_error(cookie);
+	if (ret) {
+		dev_err(dev, "remote eDMA submission error: %d\n", ret);
+		goto unmap;
+	}
+
+	dma_async_issue_pending(chan);
+
+	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(5000))) {
+		dev_err(dev, "remote eDMA transfer timeout\n");
+		dmaengine_terminate_sync(chan);
+		ret = -ETIMEDOUT;
+		goto unmap;
+	}
+
+	ret = 0;
+unmap:
+	dma_unmap_sg(dev, &sg, 1, map_dir);
+	return ret;
+}
+
+static int pci_endpoint_test_edma_write(struct pci_endpoint_test *test,
+					size_t size)
+{
+	struct pci_endpoint_test_edma *edma;
+	struct pci_dev *pdev = test->pdev;
+	struct device *dev = &pdev->dev;
+	struct pcitest_edma_info info;
+	u32 reg, crc32, peer_crc32;
+	unsigned long left;
+	int ret;
+
+	/*
+	 * Note that test->alignment does not apply here. If some vendor
+	 * dmaengine for remote use may impose some alignment restriction, we
+	 * may as well introduce another field such as
+	 * test->remote_dma_alignment.
+	 */
+	void *orig_addr __free(kfree) = kzalloc(size, GFP_KERNEL);
+	if (!orig_addr)
+		return -ENOMEM;
+
+	ret = pci_endpoint_test_remote_edma_setup(test, size);
+	if (ret)
+		return ret;
+
+	edma = test->data;
+	if (!edma) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	get_random_bytes(orig_addr, size);
+
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
+
+	memcpy_fromio(&info, edma->bar_base, sizeof(info));
+	if (le32_to_cpu(info.test_buf_size) < size) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = pci_endpoint_test_edma_xfer(test->pdev, edma, orig_addr, size,
+					  le64_to_cpu(info.test_buf_phys),
+					  DMA_MEM_TO_DEV);
+	if (ret) {
+		dev_err(dev, "pci_endpoint_test_edma_xfer error: %d\n", ret);
+		goto err;
+	}
+
+	reinit_completion(&test->irq_raised);
+
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+				 COMMAND_REMOTE_EDMA_CHECKSUM);
+
+	left = wait_for_completion_timeout(&test->irq_raised,
+					   msecs_to_jiffies(1000));
+
+	reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+
+	if (!left || !(reg & STATUS_REMOTE_EDMA_CHECKSUM_SUCCESS)) {
+		dev_err(dev, "Failed to get checksum\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	crc32 = crc32_le(~0, orig_addr, size);
+	peer_crc32 = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM);
+	if (crc32 != peer_crc32) {
+		dev_err(dev,
+			"Checksum mismatch: %#x vs %#x\n", crc32, peer_crc32);
+		ret = -EINVAL;
+	}
+err:
+	pci_endpoint_test_remote_edma_teardown(test);
+	pci_endpoint_test_edma_restore_irq(test);
+	return ret;
+}
+
+static int pci_endpoint_test_edma_read(struct pci_endpoint_test *test,
+				       size_t size)
+{
+	struct pci_endpoint_test_edma *edma;
+	struct pci_dev *pdev = test->pdev;
+	struct device *dev = &pdev->dev;
+	struct pcitest_edma_info info;
+	u32 crc32, peer_crc32;
+	int ret;
+
+	/*
+	 * Note that test->alignment does not apply here. If some vendor
+	 * dmaengine for remote use may impose some alignment restriction, we
+	 * may as well introduce another field such as
+	 * test->remote_dma_alignment.
+	 */
+	void *orig_addr __free(kfree) = kzalloc(size, GFP_KERNEL);
+	if (!orig_addr)
+		return -ENOMEM;
+
+	ret = pci_endpoint_test_remote_edma_setup(test, size);
+	if (ret)
+		return ret;
+
+	peer_crc32 = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM);
+
+	edma = test->data;
+	if (!edma) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
+
+	memcpy_fromio(&info, edma->bar_base, sizeof(info));
+	if (le32_to_cpu(info.test_buf_size) < size) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = pci_endpoint_test_edma_xfer(test->pdev, edma, orig_addr, size,
+					  le64_to_cpu(info.test_buf_phys),
+					  DMA_DEV_TO_MEM);
+	if (ret) {
+		dev_err(dev, "pci_endpoint_test_edma_xfer error: %d\n", ret);
+		goto err;
+	}
+
+	crc32 = crc32_le(~0, orig_addr, size);
+	if (crc32 != peer_crc32) {
+		dev_err(dev,
+			"Checksum mismatch: %#x vs %#x\n", crc32, peer_crc32);
+		ret = -EINVAL;
+	}
+err:
+	pci_endpoint_test_remote_edma_teardown(test);
+	pci_endpoint_test_edma_restore_irq(test);
+	return ret;
+}
+#else
+static bool pci_endpoint_test_bar_is_reserved(struct pci_endpoint_test *test,
+					      enum pci_barno barno)
+{
+	return 0;
+}
+
+static void pci_endpoint_test_remote_edma_teardown(struct pci_endpoint_test *test)
+{
+}
+
+static int pci_endpoint_test_edma_write(struct pci_endpoint_test *test,
+					size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
+static int pci_endpoint_test_edma_read(struct pci_endpoint_test *test,
+				       size_t size)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
 {
 	struct pci_endpoint_test *test = dev_id;
@@ -307,6 +924,9 @@ static int pci_endpoint_test_bar(struct pci_endpoint_test *test,
 	if (barno == test->test_reg_bar)
 		bar_size = 0x4;
 
+	if (pci_endpoint_test_bar_is_reserved(test, barno))
+		return -EOPNOTSUPP;
+
 	/*
 	 * Allocate a buffer of max size 1MB, and reuse that buffer while
 	 * iterating over the whole BAR size (which might be much larger).
@@ -354,6 +974,9 @@ static void pci_endpoint_test_bars_write_bar(struct pci_endpoint_test *test,
 	if (barno == test->test_reg_bar)
 		size = 0x4;
 
+	if (pci_endpoint_test_bar_is_reserved(test, barno))
+		return;
+
 	for (j = 0; j < size; j += 4)
 		writel_relaxed(bar_test_pattern_with_offset(barno, j),
 			       test->bar[barno] + j);
@@ -372,6 +995,9 @@ static int pci_endpoint_test_bars_read_bar(struct pci_endpoint_test *test,
 	if (barno == test->test_reg_bar)
 		size = 0x4;
 
+	if (pci_endpoint_test_bar_is_reserved(test, barno))
+		return 0;
+
 	for (j = 0; j < size; j += 4) {
 		u32 expected = bar_test_pattern_with_offset(barno, j);
 
@@ -645,6 +1271,9 @@ static int pci_endpoint_test_write(struct pci_endpoint_test *test,
 
 	size = param.size;
 
+	if (param.flags & PCITEST_FLAGS_USE_REMOTE_EDMA)
+		return pci_endpoint_test_edma_write(test, size);
+
 	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
 	if (use_dma)
 		flags |= FLAG_USE_DMA;
@@ -742,6 +1371,9 @@ static int pci_endpoint_test_read(struct pci_endpoint_test *test,
 
 	size = param.size;
 
+	if (param.flags & PCITEST_FLAGS_USE_REMOTE_EDMA)
+		return pci_endpoint_test_edma_read(test, size);
+
 	use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA);
 	if (use_dma)
 		flags |= FLAG_USE_DMA;
@@ -1139,6 +1771,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
 	if (id < 0)
 		return;
 
+	pci_endpoint_test_remote_edma_teardown(test);
 	pci_endpoint_test_release_irq(test);
 	pci_endpoint_test_free_irq_vectors(test);
 
diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h
index d6023a45a9d0..c72d999cecf7 100644
--- a/include/uapi/linux/pcitest.h
+++ b/include/uapi/linux/pcitest.h
@@ -30,7 +30,8 @@
 #define PCITEST_IRQ_TYPE_MSIX		2
 #define PCITEST_IRQ_TYPE_AUTO		3
 
-#define PCITEST_FLAGS_USE_DMA	0x00000001
+#define PCITEST_FLAGS_USE_DMA		0x00000001
+#define PCITEST_FLAGS_USE_REMOTE_EDMA	0x00000002
 
 struct pci_endpoint_test_xfer_param {
 	unsigned long size;
-- 
2.51.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ