From: Lukasz Laguna <lukasz.laguna@intel.com>
To: igt-dev@lists.freedesktop.org
Cc: marcin.bernatowicz@intel.com, piotr.piorkowski@intel.com
Subject: [PATCH 1/2] tests/intel/xe_sriov_ggtt: Add test checking GGTT accesses
Date: Tue, 4 Aug 2026 09:24:06 +0200 [thread overview]
Message-ID: <20260804072407.1090889-2-lukasz.laguna@intel.com> (raw)
In-Reply-To: <20260804072407.1090889-1-lukasz.laguna@intel.com>
From: Piotr Piórkowski <piotr.piorkowski@intel.com>
Add a test validating PTE ownership, accessibility, and modifiability
by PF and VFs.
The following scenarios are covered:
- pf-check-vfs-ids: verify VF ID assignment for each VF GGTT range,
- pf-check-vfs-pte: verify PF modifiability of VF PTEs,
- vfs-check-own-pte: verify VF modifiability of its own PTEs,
- vfs-check-other-vfs-pte: verify VF can't access other VFs PTEs,
- vfs-check-pf-pte: verify VF can't access PF PTEs.
Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
Co-developed-by: Marcin Bernatowicz <marcin.bernatowicz@linux.intel.com>
Signed-off-by: Marcin Bernatowicz <marcin.bernatowicz@linux.intel.com>
Co-developed-by: Lukasz Laguna <lukasz.laguna@intel.com>
Signed-off-by: Lukasz Laguna <lukasz.laguna@intel.com>
---
lib/xe/xe_ggtt.h | 7 +
tests/intel/xe_sriov_ggtt.c | 757 ++++++++++++++++++++++++++++++++++++
tests/meson.build | 1 +
3 files changed, 765 insertions(+)
create mode 100644 tests/intel/xe_sriov_ggtt.c
diff --git a/lib/xe/xe_ggtt.h b/lib/xe/xe_ggtt.h
index ada7ddbb0..8e0202cd7 100644
--- a/lib/xe/xe_ggtt.h
+++ b/lib/xe/xe_ggtt.h
@@ -8,6 +8,11 @@
#include <stdint.h>
+#define GGTT_PAGE_SIZE SZ_4K
+#define GGTT_TOP 0xfee00000
+
+#define XELPG_GGTT_PTE_PAT_MASK GENMASK_ULL(53, 52)
+
#define GGTT_PTE_ADDR_MASK GENMASK_ULL(45, 12)
#define TGL_GGTT_PTE_ADDR_MASK GENMASK_ULL(38, 12)
#define GGTT_PTE_ADDR_SHIFT 12
@@ -16,10 +21,12 @@
#define TGL_GGTT_PTE_VFID_MASK GENMASK_ULL(4, 2)
#define GGTT_PTE_VFID_SHIFT 2
+#define GGTT_PAGE_LM GENMASK_ULL(1, 1)
#define GGTT_PAGE_PRESENT GENMASK_ULL(0, 0)
typedef uint64_t xe_ggtt_pte_t;
typedef uint64_t xe_ggtt_pte_mask_t;
+typedef uint64_t xe_ggtt_addr_t;
xe_ggtt_pte_mask_t xe_ggtt_get_vfid_mask(int pf_fd);
xe_ggtt_pte_mask_t xe_ggtt_get_gpa_mask(int pf_fd);
diff --git a/tests/intel/xe_sriov_ggtt.c b/tests/intel/xe_sriov_ggtt.c
new file mode 100644
index 000000000..2c2184930
--- /dev/null
+++ b/tests/intel/xe_sriov_ggtt.c
@@ -0,0 +1,757 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright(c) 2026 Intel Corporation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+
+#include "drmtest.h"
+#include "igt_sriov_device.h"
+#include "intel_chipset.h"
+#include "xe/xe_ggtt.h"
+#include "xe/xe_mmio.h"
+#include "xe/xe_sriov_debugfs.h"
+#include "xe/xe_sriov_provisioning.h"
+#include "xe/xe_query.h"
+
+/**
+ * TEST: xe_sriov_ggtt
+ * Category: Core
+ * Mega feature: SR-IOV
+ * Sub-category: GGTT
+ * Functionality: GGTT isolation
+ * Description: Checks GGTT bitfields and VFs GGTT isolation
+ *
+ * SUBTEST: pf-check-vfs-ids
+ * Description:
+ * Verify that the VF ID in the GGTT PTE has been correctly assigned
+ *
+ * SUBTEST: pf-check-vfs-pte
+ * Description:
+ * Verify on PF the modifiability of GGTT PTE assigned to VFs
+ *
+ * SUBTEST: vfs-check-own-pte
+ * Description:
+ * Verify of modifiability of GGTT PTE owned by VF
+ *
+ * SUBTEST: vfs-check-other-vfs-pte
+ * Description:
+ * Verify the non-availability of VF to GGTT PTEs owned by other VFs
+ *
+ * SUBTEST: vfs-check-pf-pte
+ * Description:
+ * Verify the non-availability of VF to GGTT PTEs owned by PF
+ */
+
+IGT_TEST_DESCRIPTION("Xe tests for SR-IOV GGTT");
+
+#define SRIOV_GGTT_TC(__name) .test = __name, .name = #__name
+
+struct ggtt_test_data {
+ int pf_fd;
+ struct xe_mmio mmio;
+ struct xe_mmio pf_mmio;
+ bool verify_on_pf;
+ bool bus_master_enabled_in_test;
+ void (*set_pte)(struct xe_mmio *mmio, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t pte);
+ xe_ggtt_pte_t (*get_pte)(struct xe_mmio *mmio, uint8_t tile, xe_ggtt_addr_t addr);
+};
+
+struct pte_testcase {
+ const char *name;
+ bool (*test)(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out);
+ bool (*requires)(int pf_fd);
+};
+
+static uint64_t xe_ggtt_addr_to_pte_offset(xe_ggtt_addr_t addr)
+{
+ igt_assert_f(IS_ALIGNED(addr, GGTT_PAGE_SIZE), "GGTT address %#lx is not aligned to %#x\n",
+ addr, GGTT_PAGE_SIZE);
+
+ return (addr / GGTT_PAGE_SIZE) * sizeof(xe_ggtt_pte_t);
+}
+
+static void intel_set_pte(struct xe_mmio *mmio, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t pte)
+{
+ xe_mmio_ggtt_write(mmio, tile, xe_ggtt_addr_to_pte_offset(addr), pte);
+}
+
+#define GEN12_VF_CAP_REG 0x1901f8
+
+static void intel_set_pte_and_sync(struct xe_mmio *mmio, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t pte)
+{
+ intel_set_pte(mmio, tile, addr, pte);
+
+ /* Adding a fence by reading MMIO register to ensure memory ordering */
+ xe_mmio_tile_read32(mmio, tile, GEN12_VF_CAP_REG);
+}
+
+static xe_ggtt_pte_t intel_get_pte(struct xe_mmio *mmio, uint8_t tile, xe_ggtt_addr_t addr)
+{
+ return xe_mmio_ggtt_read(mmio, tile, xe_ggtt_addr_to_pte_offset(addr));
+}
+
+static bool has_lmem(int pf_fd)
+{
+ return xe_has_vram(pf_fd);
+}
+
+static bool has_pat(int pf_fd)
+{
+ uint16_t dev_id = intel_get_drm_devid(pf_fd);
+
+ return intel_graphics_ver(dev_id) >= IP_VER(12, 70);
+}
+
+static bool check_pte_vfid(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ unsigned int vf_id, xe_ggtt_pte_t *out)
+{
+ unsigned int val;
+
+ *out = data->get_pte(&data->mmio, tile, addr);
+ val = xe_ggtt_pte_get_vfid(data->pf_fd, *out);
+
+ return (val == vf_id);
+}
+
+static bool pte_is_value_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_mask_t mask, xe_ggtt_pte_t *out)
+{
+ xe_ggtt_pte_t original_pte;
+ xe_ggtt_pte_t write_pte;
+ xe_ggtt_pte_t read_pte;
+ bool ret = true;
+
+ original_pte = data->get_pte(&data->mmio, tile, addr);
+
+ write_pte = original_pte ^ mask;
+ data->set_pte(&data->mmio, tile, addr, write_pte);
+ read_pte = data->get_pte(&data->mmio, tile, addr);
+
+ *out = read_pte;
+
+ if ((read_pte & mask) != (write_pte & mask))
+ ret = false;
+
+ if (data->verify_on_pf) {
+ xe_ggtt_pte_t pf_read_pte = data->get_pte(&data->pf_mmio, tile, addr);
+
+ /* Double check, this time compare with value read on PF */
+ if ((pf_read_pte & mask) != (write_pte & mask))
+ ret = false;
+ }
+
+ data->set_pte(&data->mmio, tile, addr, original_pte);
+
+ return ret;
+}
+
+static bool pte_is_value_not_readable(struct ggtt_test_data *data, uint8_t tile,
+ xe_ggtt_addr_t addr, xe_ggtt_pte_mask_t mask,
+ xe_ggtt_pte_t *out)
+{
+ *out = data->get_pte(&data->mmio, tile, addr);
+
+ return (*out & mask) == 0;
+}
+
+static bool pte_not_accessible(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ uint16_t dev_id = intel_get_drm_devid(data->pf_fd);
+ xe_ggtt_pte_mask_t mask;
+ xe_ggtt_pte_t expected;
+
+ if (intel_graphics_ver(dev_id) < IP_VER(12, 10)) {
+ expected = ~0;
+ mask = ~0;
+ } else if (intel_graphics_ver(dev_id) < IP_VER(12, 70)) {
+ expected = 0;
+ mask = ~0;
+ } else {
+ expected = 0;
+ mask = GGTT_PTE_ADDR_MASK | GGTT_PTE_VFID_MASK;
+ }
+
+ *out = data->get_pte(&data->mmio, tile, addr);
+ return (*out & mask) == expected;
+}
+
+static bool pte_pat_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return pte_is_value_modifiable(data, tile, addr, XELPG_GGTT_PTE_PAT_MASK, out);
+}
+
+static bool pte_pat_not_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return !pte_pat_modifiable(data, tile, addr, out);
+}
+
+static bool pte_gpa_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return pte_is_value_modifiable(data, tile, addr, xe_ggtt_get_gpa_mask(data->pf_fd), out);
+}
+
+static bool pte_gpa_not_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return !pte_gpa_modifiable(data, tile, addr, out);
+}
+
+static bool pte_vfid_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return pte_is_value_modifiable(data, tile, addr, xe_ggtt_get_vfid_mask(data->pf_fd), out);
+}
+
+static bool pte_vfid_not_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return !pte_vfid_modifiable(data, tile, addr, out);
+}
+
+static bool pte_vfid_not_readable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return pte_is_value_not_readable(data, tile, addr,
+ xe_ggtt_get_vfid_mask(data->pf_fd), out);
+}
+
+static bool pte_lmem_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return pte_is_value_modifiable(data, tile, addr, GGTT_PAGE_LM, out);
+}
+
+static bool pte_lmem_not_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return !pte_lmem_modifiable(data, tile, addr, out);
+}
+
+static bool pte_valid_modifiable(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ xe_ggtt_pte_t *out)
+{
+ return pte_is_value_modifiable(data, tile, addr, GGTT_PAGE_PRESENT, out);
+}
+
+static bool pte_valid_not_modifiable(struct ggtt_test_data *data, uint8_t tile,
+ xe_ggtt_addr_t addr, xe_ggtt_pte_t *out)
+{
+ return !pte_valid_modifiable(data, tile, addr, out);
+}
+
+static bool run_test_on_pte(struct ggtt_test_data *data, uint8_t tile, xe_ggtt_addr_t addr,
+ const struct pte_testcase *tc, unsigned int vf_id)
+{
+ xe_ggtt_pte_t read_val;
+
+ if (!tc->test(data, tile, addr, &read_val)) {
+ igt_warn("%s failed at GGTT address %#lx on VF%d. PTE value: %#lx\n",
+ tc->name, addr, vf_id, read_val);
+ return false;
+ }
+
+ return true;
+}
+
+static bool verify_each_pte_in_range;
+
+static xe_ggtt_addr_t next_addr(xe_ggtt_addr_t addr,
+ const struct xe_sriov_provisioned_range *range)
+{
+ xe_ggtt_addr_t step, next, last_page;
+
+ if (verify_each_pte_in_range)
+ return addr + GGTT_PAGE_SIZE;
+
+ if (addr < range->start + GGTT_PAGE_SIZE)
+ step = GGTT_PAGE_SIZE;
+ else
+ step = (addr - range->start) * 2;
+
+ next = addr + step;
+
+ last_page = ALIGN_DOWN(range->end, GGTT_PAGE_SIZE);
+ if (addr < last_page && next > last_page)
+ return last_page;
+
+ return next;
+}
+
+#define for_each_pte(ggtt_addr__, ggtt_range__) \
+ for ((ggtt_addr__) = (ggtt_range__)->start; \
+ (ggtt_addr__) < (ggtt_range__)->end; \
+ (ggtt_addr__) = next_addr((ggtt_addr__), (ggtt_range__)))
+
+static bool is_testcase_supported(const struct pte_testcase *tc, struct ggtt_test_data *data)
+{
+ return !(tc->requires && !tc->requires(data->pf_fd));
+}
+
+static bool run_test_on_ggtt_range(struct ggtt_test_data *data, uint8_t tile,
+ struct xe_sriov_provisioned_range *range,
+ const struct pte_testcase *tc, unsigned int vf_id)
+{
+ xe_ggtt_addr_t addr;
+
+ igt_assert_f(IS_ALIGNED(range->start, GGTT_PAGE_SIZE),
+ "GGTT start range for VF%d (%#lx) is not aligned to %#x\n",
+ vf_id, range->start, GGTT_PAGE_SIZE);
+
+ if (!is_testcase_supported(tc, data)) {
+ igt_debug("Skip test case %s on VF%u\n", tc->name, vf_id);
+ return true;
+ }
+
+ for_each_pte(addr, range)
+ if (!run_test_on_pte(data, tile, addr, tc, vf_id))
+ return false;
+ return true;
+}
+
+static bool run_test_outside_ggtt_range(struct ggtt_test_data *data, uint8_t tile,
+ struct xe_sriov_provisioned_range *range,
+ const struct pte_testcase *tc, unsigned int vf_id)
+{
+ struct xe_sriov_provisioned_range full_range = { .start = 0, .end = GGTT_TOP};
+ xe_ggtt_addr_t addr;
+
+ if (!is_testcase_supported(tc, data)) {
+ igt_debug("Skip test case %s on VF%u\n", tc->name, vf_id);
+ return true;
+ }
+
+ for_each_pte(addr, &full_range) {
+ if (addr >= range->start && addr <= range->end)
+ continue;
+ if (!run_test_on_pte(data, tile, addr, tc, vf_id))
+ return false;
+ }
+ return true;
+}
+
+static bool skip_vf_bus_master;
+
+#define PCI_COMMAND 0x04
+#define PCI_COMMAND_MASTER REG_BIT(2)
+
+static void set_bus_master(struct ggtt_test_data *test_data, bool enable)
+{
+ struct pci_device *dev = test_data->mmio.intel_mmio.dev;
+ bool was_enabled;
+ char bdf[16];
+ uint16_t cmd;
+
+ igt_assert(dev);
+
+ if (skip_vf_bus_master)
+ return;
+
+ snprintf(bdf, sizeof(bdf), "%04x:%02x:%02x.%x",
+ dev->domain, dev->bus, dev->dev, dev->func);
+
+ pci_device_cfg_read_u16(dev, &cmd, PCI_COMMAND);
+
+ was_enabled = cmd & PCI_COMMAND_MASTER;
+
+ if (enable) {
+ if (was_enabled)
+ return;
+
+ cmd |= PCI_COMMAND_MASTER;
+ pci_device_cfg_write_u16(dev, cmd, PCI_COMMAND);
+ test_data->bus_master_enabled_in_test = true;
+
+ igt_debug("%s: Enabling Bus Master (cmd=0x%04x)\n", bdf, cmd);
+ } else {
+ if (!test_data->bus_master_enabled_in_test || !was_enabled)
+ return;
+
+ cmd &= ~PCI_COMMAND_MASTER;
+ pci_device_cfg_write_u16(dev, cmd, PCI_COMMAND);
+
+ igt_debug("%s: Disabling Bus Master (cmd=0x%04x)\n", bdf, cmd);
+ }
+}
+
+static void enable_bus_master(struct ggtt_test_data *test_data)
+{
+ set_bus_master(test_data, true);
+}
+
+static void disable_bus_master(struct ggtt_test_data *test_data)
+{
+ set_bus_master(test_data, false);
+}
+
+static void init_subtest(int pf_fd, int vf_id, uint8_t tile,
+ struct xe_sriov_provisioned_range **range,
+ struct ggtt_test_data *test_data, unsigned int num_vfs)
+{
+ uint16_t dev_id = intel_get_drm_devid(pf_fd);
+ unsigned int nr_ranges, main_gt;
+
+ igt_sriov_disable_driver_autoprobe(pf_fd);
+ igt_sriov_enable_vfs(pf_fd, num_vfs);
+
+ /* refresh PCI state */
+ igt_pci_system_reinit();
+
+ main_gt = xe_tile_get_main_gt_id(pf_fd, tile);
+ xe_sriov_pf_debugfs_read_provisioned_ranges(pf_fd, XE_SRIOV_SHARED_RES_GGTT,
+ main_gt, range, &nr_ranges);
+ igt_assert_eq(num_vfs, nr_ranges);
+
+ test_data->pf_fd = pf_fd;
+ test_data->verify_on_pf = (vf_id > 0);
+ test_data->get_pte = intel_get_pte;
+
+ if (intel_graphics_ver(dev_id) >= IP_VER(12, 7))
+ test_data->set_pte = intel_set_pte_and_sync;
+ else
+ test_data->set_pte = intel_set_pte;
+
+ xe_mmio_vf_access_init(pf_fd, vf_id, &test_data->mmio);
+ if (test_data->verify_on_pf)
+ xe_mmio_access_init(pf_fd, &test_data->pf_mmio);
+
+ enable_bus_master(test_data);
+}
+
+static void fini_subtest(struct xe_sriov_provisioned_range *range,
+ struct ggtt_test_data *test_data)
+{
+ disable_bus_master(test_data);
+
+ free(range);
+
+ if (test_data->verify_on_pf)
+ xe_mmio_access_fini(&test_data->pf_mmio);
+ xe_mmio_access_fini(&test_data->mmio);
+
+ igt_sriov_disable_vfs(test_data->pf_fd);
+}
+
+#define for_each_pte_test(tc__, testcases__) \
+ for ((tc__) = (testcases__); (tc__)->test; (tc__)++)
+
+static void pf_check_vfs_ids(int pf_fd, uint8_t tile, unsigned int num_vfs)
+{
+ struct xe_sriov_provisioned_range *range;
+ struct ggtt_test_data test_data;
+ xe_ggtt_addr_t addr;
+ xe_ggtt_pte_t out;
+ unsigned int vf;
+ bool is_correct;
+
+ init_subtest(pf_fd, 0, tile, &range, &test_data, num_vfs);
+
+ for_each_sriov_enabled_vf(pf_fd, vf_id) {
+ igt_assert_eq(vf_id, range[vf_id - 1].vf_id);
+ igt_assert_f(IS_ALIGNED(range[vf_id - 1].start, GGTT_PAGE_SIZE),
+ "GGTT start range for VF%d (%#lx) is not aligned to %#x\n",
+ vf_id, range[vf_id - 1].start, GGTT_PAGE_SIZE);
+
+ igt_debug("Checking VF%u range [%#lx-%#lx]\n",
+ vf_id, range[vf_id - 1].start, range[vf_id - 1].end);
+ for_each_pte(addr, &range[vf_id - 1]) {
+ is_correct = check_pte_vfid(&test_data, tile, addr, vf_id, &out);
+ if (!is_correct) {
+ vf = vf_id;
+ goto out;
+ }
+ }
+ }
+
+out:
+ fini_subtest(range, &test_data);
+
+ igt_fail_on_f(!is_correct,
+ "Found PTE from VF%d range with wrong VF ID: Read PTE: %#lx on address: %#lx\n",
+ vf, out, addr);
+}
+
+static void pf_check_vfs_pte(int pf_fd, uint8_t tile)
+{
+ static struct pte_testcase pte_testcases[] = {
+ { SRIOV_GGTT_TC(pte_pat_modifiable), .requires = has_pat },
+ { SRIOV_GGTT_TC(pte_gpa_modifiable) },
+ { SRIOV_GGTT_TC(pte_vfid_modifiable) },
+ { SRIOV_GGTT_TC(pte_lmem_modifiable), .requires = has_lmem },
+ { SRIOV_GGTT_TC(pte_valid_modifiable) },
+ { },
+ };
+ struct xe_sriov_provisioned_range *range;
+ struct ggtt_test_data test_data;
+ struct pte_testcase *tc;
+ int failed = 0;
+
+ init_subtest(pf_fd, 0, tile, &range, &test_data, igt_sriov_get_total_vfs(pf_fd));
+
+ for_each_sriov_vf(pf_fd, vf_id) {
+ igt_debug("Checking VF%u range [%#lx-%#lx]\n", vf_id, range[vf_id - 1].start,
+ range[vf_id - 1].end);
+ for_each_pte_test(tc, pte_testcases) {
+ igt_debug("Run '%s' check\n", tc->name);
+ if (!run_test_on_ggtt_range(&test_data, tile, &range[vf_id - 1], tc,
+ vf_id))
+ failed++;
+ }
+ }
+
+ fini_subtest(range, &test_data);
+
+ igt_fail_on_f(failed, "%s: Count of failed test cases: %d\n", __func__, failed);
+}
+
+static void vfs_check_own_pte(int pf_fd, int vf_id, uint8_t tile)
+{
+ static struct pte_testcase pte_testcases[] = {
+ { SRIOV_GGTT_TC(pte_pat_modifiable), .requires = has_pat },
+ { SRIOV_GGTT_TC(pte_gpa_modifiable) },
+ { SRIOV_GGTT_TC(pte_vfid_not_readable) },
+ { SRIOV_GGTT_TC(pte_vfid_not_modifiable) },
+ { SRIOV_GGTT_TC(pte_lmem_modifiable), .requires = has_lmem },
+ { SRIOV_GGTT_TC(pte_valid_not_modifiable) },
+ { },
+ };
+ struct xe_sriov_provisioned_range *range;
+ struct ggtt_test_data test_data;
+ struct pte_testcase *tc;
+ int failed = 0;
+
+ init_subtest(pf_fd, vf_id, tile, &range, &test_data, igt_sriov_get_total_vfs(pf_fd));
+
+ igt_debug("Checking VF%u range [%#lx-%#lx]\n", vf_id, range[vf_id - 1].start,
+ range[vf_id - 1].end);
+ for_each_pte_test(tc, pte_testcases) {
+ igt_debug("Run '%s' check\n", tc->name);
+ if (!run_test_on_ggtt_range(&test_data, tile, &range[vf_id - 1], tc, vf_id))
+ failed++;
+ }
+
+ fini_subtest(range, &test_data);
+
+ igt_fail_on_f(failed, "%s: Count of failed test cases: %d\n", __func__, failed);
+}
+
+static void vfs_check_other_vfs_pte(int pf_fd, int vf_id, uint8_t tile)
+{
+ static struct pte_testcase pte_testcases[] = {
+ { SRIOV_GGTT_TC(pte_not_accessible) },
+ { SRIOV_GGTT_TC(pte_pat_not_modifiable), .requires = has_pat },
+ { SRIOV_GGTT_TC(pte_gpa_not_modifiable) },
+ { SRIOV_GGTT_TC(pte_vfid_not_modifiable) },
+ { SRIOV_GGTT_TC(pte_lmem_not_modifiable), .requires = has_lmem },
+ { SRIOV_GGTT_TC(pte_valid_not_modifiable) },
+ { },
+ };
+ struct xe_sriov_provisioned_range *range;
+ struct ggtt_test_data test_data;
+ struct pte_testcase *tc;
+ int failed = 0;
+
+ init_subtest(pf_fd, vf_id, tile, &range, &test_data, igt_sriov_get_total_vfs(pf_fd));
+
+ for_each_pte_test(tc, pte_testcases) {
+ bool ret;
+
+ igt_debug("Run '%s' check\n", tc->name);
+ for_each_sriov_vf(pf_fd, other_vf_id) {
+ if (vf_id == other_vf_id)
+ continue;
+ igt_debug("Checking VF%u range [%#lx-%#lx]\n", other_vf_id,
+ range[other_vf_id - 1].start, range[other_vf_id - 1].end);
+ ret = run_test_on_ggtt_range(&test_data, tile, &range[other_vf_id - 1], tc,
+ vf_id);
+ if (!ret)
+ break;
+ }
+ if (!ret)
+ failed++;
+ }
+
+ fini_subtest(range, &test_data);
+
+ igt_fail_on_f(failed, "%s: Count of failed test cases: %d\n", __func__, failed);
+}
+
+static void assign_to_pf_other_vfs_ggtt(int pf_fd, uint8_t tile, struct xe_mmio *pf_mmio,
+ unsigned int skip_vf_id,
+ struct xe_sriov_provisioned_range *range)
+{
+ for_each_sriov_vf(pf_fd, vf_id) {
+ xe_ggtt_pte_t pte = GGTT_PAGE_PRESENT;
+ xe_ggtt_addr_t addr;
+
+ if (skip_vf_id == vf_id)
+ continue;
+
+ igt_debug("Set PF as owner for VF%u range [%#lx-%#lx]\n", vf_id,
+ range[vf_id - 1].start, range[vf_id - 1].end);
+ for_each_pte(addr, &range[vf_id - 1])
+ intel_set_pte(pf_mmio, tile, addr, pte);
+ }
+}
+
+static void restore_other_vfs_ggtt(int pf_fd, uint8_t tile, struct xe_mmio *pf_mmio,
+ unsigned int skip_vf_id,
+ struct xe_sriov_provisioned_range *range)
+{
+ for_each_sriov_vf(pf_fd, vf_id) {
+ xe_ggtt_pte_t pte = ((vf_id << GGTT_PTE_VFID_SHIFT) &
+ xe_ggtt_get_vfid_mask(pf_fd)) | GGTT_PAGE_PRESENT;
+ xe_ggtt_addr_t addr;
+
+ if (skip_vf_id == vf_id)
+ continue;
+
+ igt_debug("Restore the original owner for VF%u range [%#lx-%#lx]\n",
+ vf_id, range[vf_id - 1].start, range[vf_id - 1].end);
+ for_each_pte(addr, &range[vf_id - 1])
+ intel_set_pte(pf_mmio, tile, addr, pte);
+ }
+}
+
+static void vfs_check_pf_pte(int pf_fd, int vf_id, uint8_t tile)
+{
+ static struct pte_testcase pte_testcases[] = {
+ { SRIOV_GGTT_TC(pte_not_accessible) },
+ { SRIOV_GGTT_TC(pte_pat_not_modifiable), .requires = has_pat },
+ { SRIOV_GGTT_TC(pte_gpa_not_modifiable) },
+ { SRIOV_GGTT_TC(pte_vfid_not_modifiable) },
+ { SRIOV_GGTT_TC(pte_lmem_not_modifiable), .requires = has_lmem },
+ { SRIOV_GGTT_TC(pte_valid_not_modifiable) },
+ { },
+ };
+ struct xe_sriov_provisioned_range *range;
+ struct ggtt_test_data test_data;
+ struct pte_testcase *tc;
+ int failed = 0;
+
+ init_subtest(pf_fd, vf_id, tile, &range, &test_data, igt_sriov_get_total_vfs(pf_fd));
+ assign_to_pf_other_vfs_ggtt(pf_fd, tile, &test_data.pf_mmio, vf_id, range);
+
+ igt_debug("Checking GGTT outside VF%u range [%#lx-%#lx]\n",
+ vf_id, range[vf_id - 1].start, range[vf_id - 1].end);
+
+ for_each_pte_test(tc, pte_testcases) {
+ igt_debug("Run '%s' check\n", tc->name);
+ if (!run_test_outside_ggtt_range(&test_data, tile, &range[vf_id - 1], tc, vf_id))
+ failed++;
+ }
+
+ restore_other_vfs_ggtt(pf_fd, tile, &test_data.pf_mmio, vf_id, range);
+ fini_subtest(range, &test_data);
+
+ igt_fail_on_f(failed, "%s: Count of failed test cases: %d\n", __func__, failed);
+}
+
+static void skip_on_mtl_vf(int pf_fd)
+{
+ uint16_t dev_id = intel_get_drm_devid(pf_fd);
+
+ igt_skip_on_f(IS_METEORLAKE(dev_id),
+ "On MTL VF there is no access to GGTT through MMIO, skip\n");
+}
+
+static int opts_handler(int opt, int opt_index, void *data)
+{
+ switch (opt) {
+ case 'b':
+ skip_vf_bus_master = true;
+ break;
+ case 'p':
+ verify_each_pte_in_range = true;
+ break;
+ default:
+ return IGT_OPT_HANDLER_ERROR;
+ }
+
+ return IGT_OPT_HANDLER_SUCCESS;
+}
+
+static const struct option long_opts[] = {
+ { .name = "skip-vf-bme", .has_arg = false, .val = 'b', },
+ { .name = "verify-each-pte", .has_arg = false, .val = 'p', },
+ {}
+};
+
+static const char help_str[] =
+ " --skip-vf-bme\tSkip setting VF Bus Master Enable bit\n"
+ " --verify-each-pte\tVerify each PTE in range\n";
+
+int igt_main_args("", long_opts, help_str, opts_handler, NULL)
+{
+ bool autoprobe;
+ uint8_t tile;
+ int pf_fd;
+
+ igt_fixture()
+ {
+ pf_fd = drm_open_driver(DRIVER_XE);
+ igt_require(igt_sriov_is_pf(pf_fd));
+ igt_require(igt_sriov_get_enabled_vfs(pf_fd) == 0);
+ autoprobe = igt_sriov_is_driver_autoprobe_enabled(pf_fd);
+ }
+
+ igt_describe("Verify that the VF ID in the GGTT PTE has been correctly assigned");
+ igt_subtest_with_dynamic("pf-check-vfs-ids")
+ for_each_sriov_num_vfs(pf_fd, num_vfs)
+ xe_for_each_tile(pf_fd, tile)
+ igt_dynamic_f("numvfs-%u-tile-%u", num_vfs, tile)
+ pf_check_vfs_ids(pf_fd, tile, num_vfs);
+
+ igt_describe("Verify on PF the modifiability of GGTT PTE assigned to VFs");
+ igt_subtest_with_dynamic("pf-check-vfs-pte")
+ xe_for_each_tile(pf_fd, tile)
+ igt_dynamic_f("tile-%u", tile)
+ pf_check_vfs_pte(pf_fd, tile);
+
+ igt_describe("Verify of modifiability of GGTT PTE owned by VF");
+ igt_subtest_with_dynamic("vfs-check-own-pte") {
+ skip_on_mtl_vf(pf_fd);
+ for_each_sriov_vf(pf_fd, vf_id)
+ xe_for_each_tile(pf_fd, tile)
+ igt_dynamic_f("vf%u-tile-%u", vf_id, tile)
+ vfs_check_own_pte(pf_fd, vf_id, tile);
+ }
+
+ igt_describe("Verify the non-availability of VF to GGTT PTEs owned by other VFs");
+ igt_subtest_with_dynamic("vfs-check-other-vfs-pte") {
+ igt_require(igt_sriov_get_total_vfs(pf_fd) > 1);
+ skip_on_mtl_vf(pf_fd);
+ for_each_sriov_vf(pf_fd, vf_id)
+ xe_for_each_tile(pf_fd, tile)
+ igt_dynamic_f("vf%u-tile-%u", vf_id, tile)
+ vfs_check_other_vfs_pte(pf_fd, vf_id, tile);
+ }
+
+ igt_describe("Verify the non-availability of VF to GGTT PTEs owned by PF");
+ igt_subtest_with_dynamic("vfs-check-pf-pte") {
+ skip_on_mtl_vf(pf_fd);
+ for_each_sriov_vf(pf_fd, vf_id)
+ xe_for_each_tile(pf_fd, tile)
+ igt_dynamic_f("vf%u-tile-%u", vf_id, tile)
+ vfs_check_pf_pte(pf_fd, vf_id, tile);
+ }
+
+ igt_fixture() {
+ igt_sriov_disable_vfs(pf_fd);
+ /* abort to avoid execution of next tests with enabled VFs */
+ igt_abort_on_f(igt_sriov_get_enabled_vfs(pf_fd) > 0, "Failed to disable VF(s)");
+ autoprobe ? igt_sriov_enable_driver_autoprobe(pf_fd) :
+ igt_sriov_disable_driver_autoprobe(pf_fd);
+ igt_abort_on_f(autoprobe != igt_sriov_is_driver_autoprobe_enabled(pf_fd),
+ "Failed to restore sriov_drivers_autoprobe value\n");
+ drm_close_driver(pf_fd);
+ }
+}
diff --git a/tests/meson.build b/tests/meson.build
index a62f447df..1ac89bab7 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -341,6 +341,7 @@ intel_xe_progs = [
'xe_sriov_admin',
'xe_sriov_auto_provisioning',
'xe_sriov_flr',
+ 'xe_sriov_ggtt',
'xe_sriov_vfio',
'xe_sriov_scheduling',
'xe_survivability',
--
2.43.0
next prev parent reply other threads:[~2026-08-04 7:25 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 7:24 [PATCH 0/2] tests/intel/xe_sriov_ggtt: Add test checking GGTT accesses Lukasz Laguna
2026-08-04 7:24 ` Lukasz Laguna [this message]
2026-08-18 13:45 ` [PATCH 1/2] " Bernatowicz, Marcin
2026-08-04 7:24 ` [PATCH 2/2] intel-ci: Block igt@xe_sriov_ggtt.* Lukasz Laguna
2026-08-18 13:45 ` Bernatowicz, Marcin
2026-08-05 4:09 ` ✓ Xe.CI.BAT: success for tests/intel/xe_sriov_ggtt: Add test checking GGTT accesses Patchwork
2026-08-05 4:30 ` ✓ i915.CI.BAT: " Patchwork
2026-08-05 9:28 ` ✓ Xe.CI.FULL: " Patchwork
2026-08-05 19:38 ` ✗ i915.CI.Full: failure " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260804072407.1090889-2-lukasz.laguna@intel.com \
--to=lukasz.laguna@intel.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=marcin.bernatowicz@intel.com \
--cc=piotr.piorkowski@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox