Commit 6c5e6101 authored by Koichiro Den's avatar Koichiro Den Committed by Bjorn Helgaas
Browse files

PCI: endpoint: pci-epf-test: Add BAR subrange mapping test support



Extend pci-epf-test so that pci_endpoint_test can exercise BAR subrange
mapping end-to-end.

Add BAR_SUBRANGE_SETUP/CLEAR commands that program (and tear down) a
simple 2-subrange layout for a selected BAR. The endpoint deliberately
permutes the physical backing regions (swap the halves) and writes a
deterministic signature byte per subrange. This allows the RC to verify
that the submap order is actually applied, not just that reads/writes
work with an identity mapping.

Advertise CAP_SUBRANGE_MAPPING only when the underlying EPC supports
dynamic_inbound_mapping and subrange_mapping. Also bump the default BAR
sizes (BAR0-4) to 128 KiB so that split subranges are large enough to
satisfy common inbound-translation alignment constraints. E.g. for DWC
EP, the default and maximum CX_ATU_MIN_REGION_SIZE is 64 kB, so 128 KiB
is sufficient for DWC-based EP platforms for 2-subrange testing.

Signed-off-by: default avatarKoichiro Den <den@valinux.co.jp>
Signed-off-by: default avatarManivannan Sadhasivam <mani@kernel.org>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/20260124145012.2794108-7-den@valinux.co.jp
parent dd3ce166
Loading
Loading
Loading
Loading
+171 −1
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@
#define COMMAND_COPY			BIT(5)
#define COMMAND_ENABLE_DOORBELL		BIT(6)
#define COMMAND_DISABLE_DOORBELL	BIT(7)
#define COMMAND_BAR_SUBRANGE_SETUP	BIT(8)
#define COMMAND_BAR_SUBRANGE_CLEAR	BIT(9)

#define STATUS_READ_SUCCESS		BIT(0)
#define STATUS_READ_FAIL		BIT(1)
@@ -48,6 +50,10 @@
#define STATUS_DOORBELL_ENABLE_FAIL	BIT(11)
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
#define STATUS_DOORBELL_DISABLE_FAIL	BIT(13)
#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS	BIT(14)
#define STATUS_BAR_SUBRANGE_SETUP_FAIL		BIT(15)
#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS	BIT(16)
#define STATUS_BAR_SUBRANGE_CLEAR_FAIL		BIT(17)

#define FLAG_USE_DMA			BIT(0)

@@ -57,6 +63,9 @@
#define CAP_MSI				BIT(1)
#define CAP_MSIX			BIT(2)
#define CAP_INTX			BIT(3)
#define CAP_SUBRANGE_MAPPING		BIT(4)

#define PCI_EPF_TEST_BAR_SUBRANGE_NSUB	2

static struct workqueue_struct *kpcitest_workqueue;

@@ -102,7 +111,7 @@ static struct pci_epf_header test_header = {
	.interrupt_pin	= PCI_INTERRUPT_INTA,
};

static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
static size_t bar_size[] = { 131072, 131072, 131072, 131072, 131072, 1048576 };

static void pci_epf_test_dma_callback(void *param)
{
@@ -806,6 +815,155 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
	reg->status = cpu_to_le32(status);
}

static u8 pci_epf_test_subrange_sig_byte(enum pci_barno barno,
					 unsigned int subno)
{
	return 0x50 + (barno * 8) + subno;
}

static void pci_epf_test_bar_subrange_setup(struct pci_epf_test *epf_test,
					    struct pci_epf_test_reg *reg)
{
	struct pci_epf_bar_submap *submap, *old_submap;
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	struct pci_epf_bar *bar;
	unsigned int nsub = PCI_EPF_TEST_BAR_SUBRANGE_NSUB, old_nsub;
	/* reg->size carries BAR number for BAR_SUBRANGE_* commands. */
	enum pci_barno barno = le32_to_cpu(reg->size);
	u32 status = le32_to_cpu(reg->status);
	unsigned int i, phys_idx;
	size_t sub_size;
	u8 *addr;
	int ret;

	if (barno >= PCI_STD_NUM_BARS) {
		dev_err(&epf->dev, "Invalid barno: %d\n", barno);
		goto err;
	}

	/* Host side should've avoided test_reg_bar, this is a safeguard. */
	if (barno == epf_test->test_reg_bar) {
		dev_err(&epf->dev, "test_reg_bar cannot be used for subrange test\n");
		goto err;
	}

	if (!epf_test->epc_features->dynamic_inbound_mapping ||
	    !epf_test->epc_features->subrange_mapping) {
		dev_err(&epf->dev, "epc driver does not support subrange mapping\n");
		goto err;
	}

	bar = &epf->bar[barno];
	if (!bar->size || !bar->addr) {
		dev_err(&epf->dev, "bar size/addr (%zu/%p) is invalid\n",
			bar->size, bar->addr);
		goto err;
	}

	if (bar->size % nsub) {
		dev_err(&epf->dev, "BAR size %zu is not divisible by %u\n",
			bar->size, nsub);
		goto err;
	}

	sub_size = bar->size / nsub;

	submap = kcalloc(nsub, sizeof(*submap), GFP_KERNEL);
	if (!submap)
		goto err;

	for (i = 0; i < nsub; i++) {
		/* Swap the two halves so RC can verify ordering. */
		phys_idx = i ^ 1;
		submap[i].phys_addr = bar->phys_addr + (phys_idx * sub_size);
		submap[i].size = sub_size;
	}

	old_submap = bar->submap;
	old_nsub = bar->num_submap;

	bar->submap = submap;
	bar->num_submap = nsub;

	ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
	if (ret) {
		dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret);
		bar->submap = old_submap;
		bar->num_submap = old_nsub;
		kfree(submap);
		goto err;
	}
	kfree(old_submap);

	/*
	 * Fill deterministic signatures into the physical regions that
	 * each BAR subrange maps to. RC verifies these to ensure the
	 * submap order is really applied.
	 */
	addr = (u8 *)bar->addr;
	for (i = 0; i < nsub; i++) {
		phys_idx = i ^ 1;
		memset(addr + (phys_idx * sub_size),
		       pci_epf_test_subrange_sig_byte(barno, i),
		       sub_size);
	}

	status |= STATUS_BAR_SUBRANGE_SETUP_SUCCESS;
	reg->status = cpu_to_le32(status);
	return;

err:
	status |= STATUS_BAR_SUBRANGE_SETUP_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_bar_subrange_clear(struct pci_epf_test *epf_test,
					    struct pci_epf_test_reg *reg)
{
	struct pci_epf *epf = epf_test->epf;
	struct pci_epf_bar_submap *submap;
	struct pci_epc *epc = epf->epc;
	/* reg->size carries BAR number for BAR_SUBRANGE_* commands. */
	enum pci_barno barno = le32_to_cpu(reg->size);
	u32 status = le32_to_cpu(reg->status);
	struct pci_epf_bar *bar;
	unsigned int nsub;
	int ret;

	if (barno >= PCI_STD_NUM_BARS) {
		dev_err(&epf->dev, "Invalid barno: %d\n", barno);
		goto err;
	}

	bar = &epf->bar[barno];
	submap = bar->submap;
	nsub = bar->num_submap;

	if (!submap || !nsub)
		goto err;

	bar->submap = NULL;
	bar->num_submap = 0;

	ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
	if (ret) {
		bar->submap = submap;
		bar->num_submap = nsub;
		dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret);
		goto err;
	}
	kfree(submap);

	status |= STATUS_BAR_SUBRANGE_CLEAR_SUCCESS;
	reg->status = cpu_to_le32(status);
	return;

err:
	status |= STATUS_BAR_SUBRANGE_CLEAR_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_cmd_handler(struct work_struct *work)
{
	u32 command;
@@ -861,6 +1019,14 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
		pci_epf_test_disable_doorbell(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_BAR_SUBRANGE_SETUP:
		pci_epf_test_bar_subrange_setup(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_BAR_SUBRANGE_CLEAR:
		pci_epf_test_bar_subrange_clear(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	default:
		dev_err(dev, "Invalid command 0x%x\n", command);
		break;
@@ -933,6 +1099,10 @@ static void pci_epf_test_set_capabilities(struct pci_epf *epf)
	if (epf_test->epc_features->intx_capable)
		caps |= CAP_INTX;

	if (epf_test->epc_features->dynamic_inbound_mapping &&
	    epf_test->epc_features->subrange_mapping)
		caps |= CAP_SUBRANGE_MAPPING;

	reg->caps = cpu_to_le32(caps);
}