From: "Bernatowicz, Marcin" <marcin.bernatowicz@linux.intel.com>
To: Lukasz Laguna <lukasz.laguna@intel.com>, igt-dev@lists.freedesktop.org
Cc: marcin.bernatowicz@intel.com, piotr.piorkowski@intel.com
Subject: Re: [PATCH v1 2/3] tests/intel/xe_sriov_mmio_regs: Add tests to verify registers on VFs
Date: Mon, 7 Sep 2026 12:52:09 +0200 [thread overview]
Message-ID: <e648762f-0266-4a69-97c6-d16b5454ca76@linux.intel.com> (raw)
In-Reply-To: <20260826083730.2750-3-lukasz.laguna@intel.com>
On 8/26/2026 10:37 AM, Lukasz Laguna wrote:
> From: Piotr Piórkowski <piotr.piorkowski@intel.com>
>
> In case of SR-IOV, the VF has limited access to the registers. Let's add
> tests to verify that the VF actually has access only to the allowed list
> of registers, and verify that registers dedicated to VFs work properly.
>
> Signed-off-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
> Co-developed-by: Lukasz Laguna <lukasz.laguna@intel.com>
> Signed-off-by: Lukasz Laguna <lukasz.laguna@intel.com>
> ---
> tests/intel/xe_sriov_mmio_regs.c | 406 +++++++++++++++++++++++++++++++
> tests/meson.build | 1 +
> 2 files changed, 407 insertions(+)
> create mode 100644 tests/intel/xe_sriov_mmio_regs.c
>
> diff --git a/tests/intel/xe_sriov_mmio_regs.c b/tests/intel/xe_sriov_mmio_regs.c
> new file mode 100644
> index 000000000..0102660a8
> --- /dev/null
> +++ b/tests/intel/xe_sriov_mmio_regs.c
> @@ -0,0 +1,406 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright(c) 2026 Intel Corporation. All rights reserved.
> + */
> +#include "drmtest.h"
> +#include "igt_sriov_device.h"
> +#include "intel_chipset.h"
> +#include "xe/xe_mmio.h"
> +#include "xe/xe_query.h"
> +
> +/**
> + * TEST: xe_sriov_mmio_regs
> + * Category: Core
> + * Mega feature: SR-IOV
> + * Sub-category: MMIO
> + * Functionality: MMIO isolation
> + * Description: Checks MMIO isolation
> + *
> + * SUBTEST: vf-cap-reg
> + * Description:
> + * Verify that VF has access to VF capability register
> + *
> + * SUBTEST: vf-scratch-regs
> + * Description:
> + * Verify that VF has RW access to VF scratch registers
> + *
> + * SUBTEST: vf-scratch-regs-unique-instance
> + * Description:
> + * Verify that VF has its own instance of scratch registers
> + *
> + * SUBTEST: vf-not-allowed-regs
> + * Description:
> + * Verify that VF does not have access to restricted registers
> + */
> +
> +IGT_TEST_DESCRIPTION("Xe tests for SR-IOV MMIO registers");
> +
> +/*
> + * Although 8 MB is reserved for the registers, they actually use only
> + * the first 4 MB
> + */
> +#define MMIO_REGS_TILE_SIZE SZ_4M
> +
> +#define VF_CAP_REG 0x1901f8
> +#define MEDIA_VF_CAP_REG 0x19030C
> +#define IS_VF_MASK 0x1
> +
> +#define SOFT_SCRATCH_COUNT 4
> +#define SOFT_SCRATCH(n) (0x190240 + (n) * SOFT_SCRATCH_COUNT)
> +#define MEDIA_SOFT_SCRATCH(n) (0x190310 + (n) * SOFT_SCRATCH_COUNT)
> +
> +#define for_each_reg(reg_addr__) \
> + for ((reg_addr__) = 0; \
> + (reg_addr__) < (MMIO_REGS_TILE_SIZE); \
> + (reg_addr__) += 0x4)
> +
> +enum reg_access_type {
> + NO_ACCESS_OR_UNDEFINED = 0,
> + RO,
> + RW,
> +};
> +
> +struct vf_regs_allowlist {
> + uint32_t start;
> + uint32_t end;
> + uint32_t mask;
> + uint32_t expected_mask;
> + enum reg_access_type access_type;
> + bool (*requires)(int pf_fd);
> +};
> +
> +static const char *stringify_reg_access_type(enum reg_access_type access_type)
> +{
> + switch (access_type) {
> + case NO_ACCESS_OR_UNDEFINED:
> + return "NO ACCESS OR UNDEFINED";
> + case RO:
> + return "RO";
> + case RW:
> + return "RW";
> + default:
> + igt_assert(0);
> + }
> +
> + return "";
> +}
> +
> +static bool has_vf_fence(int pf_fd)
> +{
> + uint16_t dev_id = intel_get_drm_devid(pf_fd);
> +
> + return (intel_graphics_ver(dev_id) < IP_VER(12, 10));
> +}
> +
> +static bool has_memirq(int pf_fd)
> +{
> + uint16_t dev_id = intel_get_drm_devid(pf_fd);
> +
> + return (intel_graphics_ver(dev_id) >= IP_VER(12, 50));
> +}
> +
> +static bool no_memirq(int pf_fd)
> +{
> + return !has_memirq(pf_fd);
> +}
> +
> +static bool has_media_regs(int pf_fd)
> +{
> + uint16_t dev_id = intel_get_drm_devid(pf_fd);
> +
> + return (intel_graphics_ver(dev_id) >= IP_VER(12, 70));
> +}
> +
> +static enum reg_access_type check_vf_reg_access(int pf_fd, uint8_t tile,
> + struct xe_mmio *vf_mmio, uint32_t reg)
> +{
> + enum reg_access_type access = NO_ACCESS_OR_UNDEFINED;
> + uint32_t orig;
> +
> + orig = xe_mmio_tile_read32(vf_mmio, tile, reg);
> + if (orig != 0 && orig != ~0)
> + access = RO;
> +
> + xe_mmio_tile_write32(vf_mmio, tile, reg, ~orig);
> + if (xe_mmio_tile_read32(vf_mmio, tile, reg) != orig)
> + access = RW;
> +
> + xe_mmio_tile_write32(vf_mmio, tile, reg, orig);
> +
> + return access;
> +}
> +
> +static void vf_check_cap_reg(int pf_fd, unsigned int vf_id, uint8_t tile)
> +{
> + enum reg_access_type access_type;
> + struct xe_mmio vf_mmio;
> + uint32_t val;
> +
> + xe_mmio_vf_access_init(pf_fd, vf_id, &vf_mmio);
> +
> + access_type = check_vf_reg_access(pf_fd, tile, &vf_mmio, VF_CAP_REG);
> + val = xe_mmio_tile_read32(&vf_mmio, tile, VF_CAP_REG);
> +
> + xe_mmio_access_fini(&vf_mmio);
> +
> + igt_fail_on_f(access_type != RO, "VF%u capability register should be RO. Detected: %s\n",
> + vf_id, stringify_reg_access_type(access_type));
> + igt_fail_on_f(!(val & IS_VF_MASK), "VF%u capability register should report VF active\n",
> + vf_id);
> +}
> +
> +static bool check_scratch_regs_access(int pf_fd, uint8_t tile, struct xe_mmio *vf_mmio, bool media)
> +{
> + bool failed = false;
> + uint8_t i;
> +
> + for (i = 0; i < SOFT_SCRATCH_COUNT; i++) {
> + uint32_t reg = media ? MEDIA_SOFT_SCRATCH(i) : SOFT_SCRATCH(i);
> + enum reg_access_type access_type = check_vf_reg_access(pf_fd, tile, vf_mmio, reg);
> +
> + if (access_type != RW) {
> + igt_warn("%s register (%#x) should be RW. Detected: %s\n",
> + media ? "Media scratch" : "Scratch", reg,
> + stringify_reg_access_type(access_type));
> + failed = true;
> + }
> + }
> +
> + return failed;
> +}
> +
> +static void vf_check_scratch_regs(int pf_fd, unsigned int vf_id, uint8_t tile)
> +{
> + struct xe_mmio vf_mmio;
> + bool failed;
> +
> + xe_mmio_vf_access_init(pf_fd, vf_id, &vf_mmio);
> +
> + failed = check_scratch_regs_access(pf_fd, tile, &vf_mmio, false);
> + if (xe_has_media_gt(pf_fd))
> + failed |= check_scratch_regs_access(pf_fd, tile, &vf_mmio, true);
> +
> + xe_mmio_access_fini(&vf_mmio);
> +
> + igt_fail_on_f(failed, "At least one of VF%u soft scratch register is not RW.\n", vf_id);
> +}
> +
> +static uint32_t get_vf_regs_stride(int pf_fd)
> +{
> + uint16_t dev_id = intel_get_drm_devid(pf_fd);
> +
> + return intel_graphics_ver(dev_id) > IP_VER(12, 0) ? 0x400 : 0x1000;
> +}
> +
> +static uint32_t vf_reg_to_pf(int pf_fd, unsigned int vf_id, uint32_t addr)
> +{
> + uint32_t stride = get_vf_regs_stride(pf_fd);
> +
> + return addr + stride * vf_id;
> +}
> +
> +static bool check_scratch_regs_uniqueness(int pf_fd, unsigned int vf_id, uint8_t tile,
> + struct xe_mmio *pf_mmio, struct xe_mmio *vf_mmio,
> + bool media)
> +{
> + unsigned int total_vfs = igt_sriov_get_total_vfs(pf_fd);
> + bool failed = false;
> + unsigned int i, j;
> +
> + for (i = 0; i < SOFT_SCRATCH_COUNT; i++) {
> + uint32_t reg = media ? MEDIA_SOFT_SCRATCH(i) : SOFT_SCRATCH(i);
> + uint32_t cached[total_vfs + 1];
> + uint32_t write, read;
> + char buf[8];
> +
> + for (j = 0; j <= total_vfs; j++)
> + cached[j] = xe_mmio_tile_read32(pf_mmio, tile,
> + vf_reg_to_pf(pf_fd, j, reg));
> +
> + write = ~cached[vf_id];
> + xe_mmio_tile_write32(vf_mmio, tile, reg, write);
> + igt_assert(xe_mmio_tile_read32(vf_mmio, tile, reg) == write);
> +
> + for (j = 0; j <= total_vfs; j++) {
> + if (j == vf_id)
> + continue;
> +
> + read = xe_mmio_tile_read32(pf_mmio, tile, vf_reg_to_pf(pf_fd, j, reg));
> + if (read != cached[j]) {
> + igt_warn("Unexpected value (%#x vs %#x) of %s %sscratch register (%#x) after VF%u write (%#x) to its own register\n",
> + read, cached[j], igt_sriov_function_name(j, buf,
> + sizeof(buf)), media ? "media " : "", reg, vf_id, write);
> + xe_mmio_tile_write32(pf_mmio, tile, vf_reg_to_pf(pf_fd, j, reg),
> + cached[j]);
> + failed = true;
> + }
> + }
> +
> + xe_mmio_tile_write32(vf_mmio, tile, reg, cached[vf_id]);
> + }
> +
> + return failed;
> +}
> +
> +static void vf_scratch_regs_unique_instance(int pf_fd, unsigned int vf_id, uint8_t tile)
> +{
> + struct xe_mmio pf_mmio, vf_mmio;
> + bool failed;
> +
> + xe_mmio_vf_access_init(pf_fd, 0, &pf_mmio);
> + xe_mmio_vf_access_init(pf_fd, vf_id, &vf_mmio);
> +
> + failed = check_scratch_regs_uniqueness(pf_fd, vf_id, tile, &pf_mmio, &vf_mmio, false);
> + if (xe_has_media_gt(pf_fd))
> + failed |= check_scratch_regs_uniqueness(pf_fd, vf_id, tile, &pf_mmio, &vf_mmio,
> + true);
> +
> + xe_mmio_access_fini(&vf_mmio);
> + xe_mmio_access_fini(&pf_mmio);
> +
> + igt_fail_on_f(failed, "VF%u write to scratch register changed the value of at least one other VF scratch register\n",
> + vf_id);
> +}
> +
> +/* XXX: Keep sorted */
> +static const struct vf_regs_allowlist allowlist[] = {
> + { .start = 0x100000, .end = 0x10001c, .requires = has_vf_fence },
> + { .start = 0x190010, .end = 0x190010, .requires = no_memirq },
> + { .start = 0x190018, .end = 0x19001C, .requires = no_memirq },
> + { .start = 0x190030, .end = 0x190048, .requires = no_memirq },
> + { .start = 0x190060, .end = 0x190064, .requires = no_memirq },
> + { .start = 0x190070, .end = 0x190074, .requires = no_memirq },
> + { .start = 0x190090, .end = 0x190090, .requires = no_memirq },
> + { .start = 0x1900a0, .end = 0x1900a0, .requires = no_memirq },
> + { .start = 0x1900a8, .end = 0x1900ac, .requires = no_memirq },
> + { .start = 0x1900b0, .end = 0x1900b4, .requires = no_memirq },
> + { .start = 0x1900d0, .end = 0x1900d4, .requires = no_memirq },
> + { .start = 0x1900e8, .end = 0x1900ec, .requires = no_memirq },
> + { .start = 0x1900f0, .end = 0x1900f4, .requires = no_memirq },
> + { .start = 0x190100, .end = 0x190100, .requires = no_memirq },
> + { .start = 0x1901f0, .end = 0x1901f0 },
> + { .start = 0x1901f8, .end = 0x1901f8 },
> + { .start = 0x190240, .end = 0x19024c },
> + { .start = 0x190300, .end = 0x190300 },
> + { .start = 0x190304, .end = 0x190304, .requires = has_media_regs },
> + { .start = 0x19030c, .end = 0x19031c, .requires = has_media_regs },
> +};
> +
> +static int addr_range_cmp(const void *addr, const void *range)
> +{
> + if (*(uint32_t *)addr < ((const struct vf_regs_allowlist *)range)->start)
> + return -1;
> + else if (*(uint32_t *)addr > ((const struct vf_regs_allowlist *)range)->end)
> + return 1;
> + else
> + return 0;
> +}
> +
> +static bool skip_if_on_whitelist(int pf_fd, uint32_t reg_addr)
> +{
> + struct vf_regs_allowlist *item;
> +
> + if (reg_addr < allowlist[0].start && reg_addr > allowlist[ARRAY_SIZE(allowlist) - 1].end)
> + return false;
> +
> + item = bsearch(®_addr, &allowlist[0], ARRAY_SIZE(allowlist), sizeof(allowlist[0]),
> + addr_range_cmp);
> + if (item) {
> + if (item->requires)
> + return (item->requires(pf_fd)) ? true : false;
> + else
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static void vf_check_not_allowed_regs(int pf_fd, unsigned int vf_id, uint8_t tile)
> +{
> + struct xe_mmio vf_mmio;
> + bool failed = false;
> + uint32_t reg;
> +
> + xe_mmio_vf_access_init(pf_fd, vf_id, &vf_mmio);
> +
> + for_each_reg(reg) {
> + enum reg_access_type access_type;
> +
> + if (skip_if_on_whitelist(pf_fd, reg))
> + continue;
> +
> + access_type = check_vf_reg_access(pf_fd, tile, &vf_mmio, reg);
> + if (access_type != NO_ACCESS_OR_UNDEFINED) {
> + igt_warn("VF%u register (%#x) shouldn't be %s.\n", vf_id, reg,
> + stringify_reg_access_type(access_type));
> + failed = true;
> + }
> + }
> +
> + xe_mmio_access_fini(&vf_mmio);
> +
> + igt_fail_on_f(failed,
> + "At least one of VF%u register, outside the allowlist, is accessible\n",
> + vf_id);
> +}
> +
> +int igt_main()
> +{
> + 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_sriov_disable_driver_autoprobe(pf_fd);
> + igt_sriov_enable_vfs(pf_fd, igt_sriov_get_total_vfs(pf_fd));
Missing igt_sriov_install_exit_handler
> + }
> +
> + igt_describe("Verify that VF has access to VF capability register");
> + igt_subtest_with_dynamic("vf-cap-reg") {
> + 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)
> + vf_check_cap_reg(pf_fd, vf_id, tile);
> + }
> +
> + igt_describe("Verify that VF has RW access to VF scratch registers");
> + igt_subtest_with_dynamic("vf-scratch-regs") {
> + 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)
> + vf_check_scratch_regs(pf_fd, vf_id, tile);
> + }
> +
> + igt_describe("Verify that VF has its own instance of scratch registers");
> + igt_subtest_with_dynamic("vf-scratch-regs-unique-instance") {
> + 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)
> + vf_scratch_regs_unique_instance(pf_fd, vf_id, tile);
> + }
> +
> + igt_describe("Verify that VF does not have access to restricted registers");
> + igt_subtest_with_dynamic("vf-not-allowed-regs") {
> + 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)
> + vf_check_not_allowed_regs(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 1ac89bab7..3ddf4a418 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -342,6 +342,7 @@ intel_xe_progs = [
> 'xe_sriov_auto_provisioning',
> 'xe_sriov_flr',
> 'xe_sriov_ggtt',
> + 'xe_sriov_mmio_regs',
> 'xe_sriov_vfio',
> 'xe_sriov_scheduling',
> 'xe_survivability',
next prev parent reply other threads:[~2026-09-07 10:52 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-26 8:37 [PATCH v1 0/3] tests/intel/xe_sriov_mmio_regs: Add tests to verify registers on VFs Lukasz Laguna
2026-08-26 8:37 ` [PATCH v1 1/3] lib/igt_sriov_device: Add helper to get SR-IOV function name Lukasz Laguna
2026-08-26 8:37 ` [PATCH v1 2/3] tests/intel/xe_sriov_mmio_regs: Add tests to verify registers on VFs Lukasz Laguna
2026-09-07 10:52 ` Bernatowicz, Marcin [this message]
2026-08-26 8:37 ` [PATCH v1 3/3] intel-ci: Block igt@xe_sriov_mmio_regs.* Lukasz Laguna
2026-08-26 9:40 ` ✓ Xe.CI.BAT: success for tests/intel/xe_sriov_mmio_regs: Add tests to verify registers on VFs Patchwork
2026-08-26 9:48 ` ✓ i915.CI.BAT: " Patchwork
2026-08-26 10:49 ` ✓ Xe.CI.FULL: " Patchwork
2026-08-26 13:23 ` ✗ 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=e648762f-0266-4a69-97c6-d16b5454ca76@linux.intel.com \
--to=marcin.bernatowicz@linux.intel.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=lukasz.laguna@intel.com \
--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