Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

misc: pci_endpoint_test: Add BAR subrange mapping test case

Add a new PCITEST_BAR_SUBRANGE ioctl to exercise EPC BAR subrange
mapping end-to-end.

The test programs a simple 2-subrange layout on the endpoint (via
pci-epf-test) and verifies that:
- the endpoint-provided per-subrange signature bytes are observed at
the expected PCIe BAR offsets, and
- writes to each subrange are routed to the correct backing region
(i.e. the submap order is applied rather than accidentally working
due to an identity mapping).

Return -EOPNOTSUPP when the endpoint does not advertise subrange
mapping, -ENODATA when the BAR is disabled, and -EBUSY when the BAR is
reserved for the test register space.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/20260124145012.2794108-8-den@valinux.co.jp

authored by

Koichiro Den and committed by
Bjorn Helgaas
8cf82bb5 6c5e6101

+203 -1
+202 -1
drivers/misc/pci_endpoint_test.c
··· 39 39 #define COMMAND_COPY BIT(5) 40 40 #define COMMAND_ENABLE_DOORBELL BIT(6) 41 41 #define COMMAND_DISABLE_DOORBELL BIT(7) 42 + #define COMMAND_BAR_SUBRANGE_SETUP BIT(8) 43 + #define COMMAND_BAR_SUBRANGE_CLEAR BIT(9) 42 44 43 45 #define PCI_ENDPOINT_TEST_STATUS 0x8 44 46 #define STATUS_READ_SUCCESS BIT(0) ··· 57 55 #define STATUS_DOORBELL_ENABLE_FAIL BIT(11) 58 56 #define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12) 59 57 #define STATUS_DOORBELL_DISABLE_FAIL BIT(13) 58 + #define STATUS_BAR_SUBRANGE_SETUP_SUCCESS BIT(14) 59 + #define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15) 60 + #define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16) 61 + #define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17) 60 62 61 63 #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c 62 64 #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 ··· 83 77 #define CAP_MSI BIT(1) 84 78 #define CAP_MSIX BIT(2) 85 79 #define CAP_INTX BIT(3) 80 + #define CAP_SUBRANGE_MAPPING BIT(4) 86 81 87 82 #define PCI_ENDPOINT_TEST_DB_BAR 0x34 88 83 #define PCI_ENDPOINT_TEST_DB_OFFSET 0x38 ··· 106 99 #define PCI_DEVICE_ID_RENESAS_R8A779F0 0x0031 107 100 108 101 #define PCI_DEVICE_ID_ROCKCHIP_RK3588 0x3588 102 + 103 + #define PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB 2 109 104 110 105 static DEFINE_IDA(pci_endpoint_test_ida); 111 106 ··· 421 412 } 422 413 423 414 return 0; 415 + } 416 + 417 + static u8 pci_endpoint_test_subrange_sig_byte(enum pci_barno barno, 418 + unsigned int subno) 419 + { 420 + return 0x50 + (barno * 8) + subno; 421 + } 422 + 423 + static u8 pci_endpoint_test_subrange_test_byte(enum pci_barno barno, 424 + unsigned int subno) 425 + { 426 + return 0xa0 + (barno * 8) + subno; 427 + } 428 + 429 + static int pci_endpoint_test_bar_subrange_cmd(struct pci_endpoint_test *test, 430 + enum pci_barno barno, u32 command, 431 + u32 ok_bit, u32 fail_bit) 432 + { 433 + struct pci_dev *pdev = test->pdev; 434 + struct device *dev = &pdev->dev; 435 + int irq_type = test->irq_type; 436 + u32 status; 437 + 438 + if (irq_type < PCITEST_IRQ_TYPE_INTX || 439 + irq_type > PCITEST_IRQ_TYPE_MSIX) { 440 + dev_err(dev, "Invalid IRQ type\n"); 441 + return -EINVAL; 442 + } 443 + 444 + reinit_completion(&test->irq_raised); 445 + 446 + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); 447 + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); 448 + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); 449 + /* Reuse SIZE as a command parameter: bar number. */ 450 + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, barno); 451 + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, command); 452 + 453 + if (!wait_for_completion_timeout(&test->irq_raised, 454 + msecs_to_jiffies(1000))) 455 + return -ETIMEDOUT; 456 + 457 + status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); 458 + if (status & fail_bit) 459 + return -EIO; 460 + 461 + if (!(status & ok_bit)) 462 + return -EIO; 463 + 464 + return 0; 465 + } 466 + 467 + static int pci_endpoint_test_bar_subrange_setup(struct pci_endpoint_test *test, 468 + enum pci_barno barno) 469 + { 470 + return pci_endpoint_test_bar_subrange_cmd(test, barno, 471 + COMMAND_BAR_SUBRANGE_SETUP, 472 + STATUS_BAR_SUBRANGE_SETUP_SUCCESS, 473 + STATUS_BAR_SUBRANGE_SETUP_FAIL); 474 + } 475 + 476 + static int pci_endpoint_test_bar_subrange_clear(struct pci_endpoint_test *test, 477 + enum pci_barno barno) 478 + { 479 + return pci_endpoint_test_bar_subrange_cmd(test, barno, 480 + COMMAND_BAR_SUBRANGE_CLEAR, 481 + STATUS_BAR_SUBRANGE_CLEAR_SUCCESS, 482 + STATUS_BAR_SUBRANGE_CLEAR_FAIL); 483 + } 484 + 485 + static int pci_endpoint_test_bar_subrange(struct pci_endpoint_test *test, 486 + enum pci_barno barno) 487 + { 488 + u32 nsub = PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB; 489 + struct device *dev = &test->pdev->dev; 490 + size_t sub_size, buf_size; 491 + resource_size_t bar_size; 492 + void __iomem *bar_addr; 493 + void *read_buf = NULL; 494 + int ret, clear_ret; 495 + size_t off, chunk; 496 + u32 i, exp, val; 497 + u8 pattern; 498 + 499 + if (!(test->ep_caps & CAP_SUBRANGE_MAPPING)) 500 + return -EOPNOTSUPP; 501 + 502 + /* 503 + * The test register BAR is not safe to reprogram and write/read 504 + * over its full size. BAR_TEST already special-cases it to a tiny 505 + * range. For subrange mapping tests, let's simply skip it. 506 + */ 507 + if (barno == test->test_reg_bar) 508 + return -EBUSY; 509 + 510 + bar_size = pci_resource_len(test->pdev, barno); 511 + if (!bar_size) 512 + return -ENODATA; 513 + 514 + bar_addr = test->bar[barno]; 515 + if (!bar_addr) 516 + return -ENOMEM; 517 + 518 + ret = pci_endpoint_test_bar_subrange_setup(test, barno); 519 + if (ret) 520 + return ret; 521 + 522 + if (bar_size % nsub || bar_size / nsub > SIZE_MAX) { 523 + ret = -EINVAL; 524 + goto out_clear; 525 + } 526 + 527 + sub_size = bar_size / nsub; 528 + if (sub_size < sizeof(u32)) { 529 + ret = -ENOSPC; 530 + goto out_clear; 531 + } 532 + 533 + /* Limit the temporary buffer size */ 534 + buf_size = min_t(size_t, sub_size, SZ_1M); 535 + 536 + read_buf = kmalloc(buf_size, GFP_KERNEL); 537 + if (!read_buf) { 538 + ret = -ENOMEM; 539 + goto out_clear; 540 + } 541 + 542 + /* 543 + * Step 1: verify EP-provided signature per subrange. This detects 544 + * whether the EP actually applied the submap order. 545 + */ 546 + for (i = 0; i < nsub; i++) { 547 + exp = (u32)pci_endpoint_test_subrange_sig_byte(barno, i) * 548 + 0x01010101U; 549 + val = ioread32(bar_addr + (i * sub_size)); 550 + if (val != exp) { 551 + dev_err(dev, 552 + "BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n", 553 + barno, i, (size_t)i * sub_size, exp, val); 554 + ret = -EIO; 555 + goto out_clear; 556 + } 557 + val = ioread32(bar_addr + (i * sub_size) + sub_size - sizeof(u32)); 558 + if (val != exp) { 559 + dev_err(dev, 560 + "BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n", 561 + barno, i, 562 + ((size_t)i * sub_size) + sub_size - sizeof(u32), 563 + exp, val); 564 + ret = -EIO; 565 + goto out_clear; 566 + } 567 + } 568 + 569 + /* Step 2: write unique pattern per subrange (write all first). */ 570 + for (i = 0; i < nsub; i++) { 571 + pattern = pci_endpoint_test_subrange_test_byte(barno, i); 572 + memset_io(bar_addr + (i * sub_size), pattern, sub_size); 573 + } 574 + 575 + /* Step 3: read back and verify (read all after all writes). */ 576 + for (i = 0; i < nsub; i++) { 577 + pattern = pci_endpoint_test_subrange_test_byte(barno, i); 578 + for (off = 0; off < sub_size; off += chunk) { 579 + void *bad; 580 + 581 + chunk = min_t(size_t, buf_size, sub_size - off); 582 + memcpy_fromio(read_buf, bar_addr + (i * sub_size) + off, 583 + chunk); 584 + bad = memchr_inv(read_buf, pattern, chunk); 585 + if (bad) { 586 + size_t bad_off = (u8 *)bad - (u8 *)read_buf; 587 + 588 + dev_err(dev, 589 + "BAR%d subrange%u data mismatch @%#zx (pattern %#02x)\n", 590 + barno, i, (size_t)i * sub_size + off + bad_off, 591 + pattern); 592 + ret = -EIO; 593 + goto out_clear; 594 + } 595 + } 596 + } 597 + 598 + out_clear: 599 + kfree(read_buf); 600 + clear_ret = pci_endpoint_test_bar_subrange_clear(test, barno); 601 + return ret ?: clear_ret; 424 602 } 425 603 426 604 static int pci_endpoint_test_intx_irq(struct pci_endpoint_test *test) ··· 1132 936 1133 937 switch (cmd) { 1134 938 case PCITEST_BAR: 939 + case PCITEST_BAR_SUBRANGE: 1135 940 bar = arg; 1136 941 if (bar <= NO_BAR || bar > BAR_5) 1137 942 goto ret; 1138 943 if (is_am654_pci_dev(pdev) && bar == BAR_0) 1139 944 goto ret; 1140 - ret = pci_endpoint_test_bar(test, bar); 945 + 946 + if (cmd == PCITEST_BAR) 947 + ret = pci_endpoint_test_bar(test, bar); 948 + else 949 + ret = pci_endpoint_test_bar_subrange(test, bar); 1141 950 break; 1142 951 case PCITEST_BARS: 1143 952 ret = pci_endpoint_test_bars(test);
+1
include/uapi/linux/pcitest.h
··· 22 22 #define PCITEST_GET_IRQTYPE _IO('P', 0x9) 23 23 #define PCITEST_BARS _IO('P', 0xa) 24 24 #define PCITEST_DOORBELL _IO('P', 0xb) 25 + #define PCITEST_BAR_SUBRANGE _IO('P', 0xc) 25 26 #define PCITEST_CLEAR_IRQ _IO('P', 0x10) 26 27 27 28 #define PCITEST_IRQ_TYPE_UNDEFINED -1