#include <drm/drm_drv.h>
#include <drm/drm_kunit_helpers.h>
+#include <kunit/static_stub.h>
#include <kunit/test.h>
#include "regs/xe_gt_regs.h"
#include "regs/xe_reg_defs.h"
#include "xe_device.h"
#include "xe_device_types.h"
+#include "xe_gt_mcr.h"
#include "xe_kunit_helpers.h"
#include "xe_pci_test.h"
#include "xe_reg_sr.h"
#include "xe_rtp.h"
-#define REGULAR_REG1 XE_REG(1)
-#define REGULAR_REG2 XE_REG(2)
-#define REGULAR_REG3 XE_REG(3)
-#define MCR_REG1 XE_REG_MCR(1)
-#define MCR_REG2 XE_REG_MCR(2)
-#define MCR_REG3 XE_REG_MCR(3)
-#define MASKED_REG1 XE_REG(1, XE_REG_OPTION_MASKED)
+#define REGULAR_REG1 XE_REG(1)
+#define REGULAR_REG2 XE_REG(2)
+#define REGULAR_REG3 XE_REG(3)
+#define REGULAR_REG4 XE_REG(4)
+#define BAD_REGULAR_REG5 XE_REG(5)
+#define MCR_REG1 XE_REG_MCR(1)
+#define MCR_REG2 XE_REG_MCR(2)
+#define MCR_REG3 XE_REG_MCR(3)
+#define BAD_MCR_REG4 XE_REG_MCR(4)
+#define MCR_REG5 XE_REG_MCR(5)
+#define MASKED_REG1 XE_REG(1, XE_REG_OPTION_MASKED)
#undef XE_REG_MCR
#define XE_REG_MCR(...) XE_REG(__VA_ARGS__, .mcr = 1)
const struct xe_rtp_entry *entries;
};
+static bool fake_xe_gt_mcr_check_reg(struct xe_gt *gt, struct xe_reg reg)
+{
+ /*
+ * All supported platforms in this imaginary setup will always have REG4
+ * as a non-MCR register and REG5 as MCR, meaning that BAD_MCR_REG4 and
+ * BAD_REGULAR_REG5 represent programming errors to be captured by our
+ * tests.
+ */
+ if (reg.raw == BAD_REGULAR_REG5.raw)
+ return true;
+
+ if (reg.raw == BAD_MCR_REG4.raw)
+ return false;
+
+ return reg.mcr;
+}
+
static bool match_yes(const struct xe_device *xe, const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
{}
},
},
+ {
+ .name = "bad-mcr-reg-forced-to-regular",
+ .expected_reg = REGULAR_REG4,
+ .expected_set_bits = REG_BIT(0),
+ .expected_clr_bits = REG_BIT(0),
+ .expected_active = BIT(0),
+ .expected_count_sr_entries = 1,
+ .expected_sr_errors = 1,
+ .entries = (const struct xe_rtp_entry_sr[]) {
+ { XE_RTP_NAME("bad-mcr-regular-reg"),
+ XE_RTP_RULES(FUNC(match_yes)),
+ XE_RTP_ACTIONS(SET(BAD_MCR_REG4, REG_BIT(0)))
+ },
+ {}
+ },
+ },
+ {
+ .name = "bad-regular-reg-forced-to-mcr",
+ .expected_reg = MCR_REG5,
+ .expected_set_bits = REG_BIT(0),
+ .expected_clr_bits = REG_BIT(0),
+ .expected_active = BIT(0),
+ .expected_count_sr_entries = 1,
+ .expected_sr_errors = 1,
+ .entries = (const struct xe_rtp_entry_sr[]) {
+ { XE_RTP_NAME("bad-regular-reg"),
+ XE_RTP_RULES(FUNC(match_yes)),
+ XE_RTP_ACTIONS(SET(BAD_REGULAR_REG5, REG_BIT(0)))
+ },
+ {}
+ },
+ },
};
static void xe_rtp_process_to_sr_tests(struct kunit *test)
xe->drm.dev = dev;
test->priv = xe;
+ kunit_activate_static_stub(test, xe_gt_mcr_check_reg, fake_xe_gt_mcr_check_reg);
+
return 0;
}
* Copyright © 2022 Intel Corporation
*/
+#include <kunit/static_stub.h>
+#include <kunit/visibility.h>
+
#include "xe_gt_mcr.h"
#include "regs/xe_gt_regs.h"
/* Mark instance 0 as initialized, we need this early for VRAM and CCS probe. */
gt->steering[INSTANCE0].initialized = true;
}
+EXPORT_SYMBOL_IF_KUNIT(xe_gt_mcr_init_early);
/**
* xe_gt_mcr_init - Normal initialization of the MCR support
return false;
}
+/*
+ * xe_gt_mcr_check_reg - check if a register is recognized by this GT as MCR
+ * @gt: GT structure
+ * @reg: The register to check
+ *
+ * Returns true if the register offset falls within one of the MMIO ranges
+ * classified as MCR for the GT.
+ */
+bool xe_gt_mcr_check_reg(struct xe_gt *gt, struct xe_reg reg)
+{
+ KUNIT_STATIC_STUB_REDIRECT(xe_gt_mcr_check_reg, gt, reg);
+
+ for (int type = 0; type <= IMPLICIT_STEERING; type++)
+ if (reg_in_steering_type_ranges(gt, reg, type))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_IF_KUNIT(xe_gt_mcr_check_reg);
+
/*
* xe_gt_mcr_get_nonterminated_steering - find group/instance values that
* will steer a register to a non-terminated instance
#endif
}
+static struct xe_reg sanitize_mcr(struct xe_reg_sr *sr,
+ const struct xe_reg_sr_entry *e,
+ struct xe_gt *gt)
+{
+ struct xe_reg reg = e->reg;
+ bool is_mcr;
+
+ /*
+ * We need the gt structure to check MCR ranges.
+ */
+ if (!gt)
+ return reg;
+
+ is_mcr = xe_gt_mcr_check_reg(gt, reg);
+
+ if (is_mcr && !reg.mcr) {
+ reg.mcr = 1;
+ xe_gt_notice(gt, "xe_reg_sr_entry using non-MCR register for address 0x%x, forcing MCR\n",
+ reg.addr);
+ reg_sr_inc_error(sr);
+ }
+
+ if (!is_mcr && reg.mcr) {
+ reg.mcr = 0;
+ xe_gt_notice(gt, "xe_reg_sr_entry using MCR register for address 0x%x, forcing non-MCR\n",
+ reg.addr);
+ reg_sr_inc_error(sr);
+ }
+
+ return reg;
+}
+
int xe_reg_sr_add(struct xe_reg_sr *sr,
const struct xe_reg_sr_entry *e,
struct xe_gt *gt)
{
unsigned long idx = e->reg.addr;
struct xe_reg_sr_entry *pentry = xa_load(&sr->xa, idx);
+ struct xe_reg reg;
int ret;
+ reg = sanitize_mcr(sr, e, gt);
+
if (pentry) {
if (!compatible_entries(pentry, e)) {
ret = -EINVAL;
}
*pentry = *e;
+ pentry->reg = reg;
ret = xa_err(xa_store(&sr->xa, idx, pentry, GFP_KERNEL));
if (ret)
goto fail_free;