From: Alex Hung <[email protected]>
[WHAT]
Add KUnit tests for dm_dmub_get_vbios_bounding_box(), covering the
successful handshake on every ASIC that has a bounding box, the GPU
memory allocation failure path, and both GPINT timeout paths. This also
covers dm_dmub_send_vbios_gpint_command().
[HOW]
Extend the fake CGS device with a fail_on_write hook so a chosen GPINT
write is never acked, which drives the timeout paths without waiting on
real firmware. Reuse the buffer object indirection added for
dm_allocate_gpu_mem() with a fake allocator, since the real path needs a
live TTM device.
The success test walks every ASIC with a bounding box and confirms a
non-zero GART buffer is allocated and that all four address words plus
the copy request are sent. The timeout tests confirm the buffer is
released and the allocation list left empty.
Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
.../amdgpu_dm/tests/amdgpu_dm_dmub_test.c | 187 ++++++++++++++++++
1 file changed, 187 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
index 4bba282abd69d..cda048e8297a0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
@@ -1353,6 +1353,7 @@ static void dm_test_abort_fused_io_no_dmub_srv(struct kunit *test)
* at the start of struct cgs_device.
*/
+#define DM_TEST_GPINT_REG (0x34c0 + 0x01f8)
#define DM_TEST_GPINT_STATUS_MASK 0xF0000000U
struct dm_test_cgs_ctx {
@@ -1362,6 +1363,8 @@ struct dm_test_cgs_ctx {
unsigned int read_calls;
unsigned int last_write_offset;
unsigned int last_read_offset;
+ /* 1-based index of the write that must never be acked; 0 acks all */
+ unsigned int fail_on_write;
};
static struct dm_test_cgs_ctx dm_test_cgs;
@@ -1372,6 +1375,9 @@ static uint32_t dm_test_cgs_read_register(struct cgs_device *cgs_device,
dm_test_cgs.read_calls++;
dm_test_cgs.last_read_offset = offset;
+ if (dm_test_cgs.fail_on_write == dm_test_cgs.write_calls)
+ return ~dm_test_cgs.last_write;
+
/* The firmware acks a GPINT by clearing the status nibble. */
return dm_test_cgs.last_write & ~DM_TEST_GPINT_STATUS_MASK;
}
@@ -1389,13 +1395,89 @@ static const struct cgs_ops dm_test_cgs_ops = {
.write_register = dm_test_cgs_write_register,
};
+/*
+ * Fake buffer object allocator: amdgpu_bo_create_kernel() and
+ * amdgpu_bo_free_kernel() need a live TTM device, so dm_allocate_gpu_mem() is
+ * routed through this fake.
+ */
+
+#define DM_TEST_FAKE_GPU_ADDR 0x1234ABCD0000ULL
+
+struct dm_test_bo_ctx {
+ void *cpu_ptr;
+ int create_ret;
+ unsigned int create_calls;
+ unsigned int free_calls;
+ unsigned long create_size;
+ u32 create_domain;
+};
+
+static struct dm_test_bo_ctx dm_test_bo;
+
+static int dm_test_bo_create_kernel(struct amdgpu_device *adev, unsigned long size,
+ int align, u32 domain, struct amdgpu_bo **bo_ptr,
+ u64 *gpu_addr, void **cpu_addr)
+{
+ dm_test_bo.create_calls++;
+ dm_test_bo.create_size = size;
+ dm_test_bo.create_domain = domain;
+
+ if (dm_test_bo.create_ret)
+ return dm_test_bo.create_ret;
+
+ *gpu_addr = DM_TEST_FAKE_GPU_ADDR;
+ *cpu_addr = dm_test_bo.cpu_ptr;
+
+ return 0;
+}
+
+static void dm_test_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr, void **cpu_addr)
+{
+ dm_test_bo.free_calls++;
+
+ *bo = NULL;
+ *gpu_addr = 0;
+ *cpu_addr = NULL;
+}
+
+static const struct amdgpu_dm_services_kunit_ops dm_test_services_ops = {
+ .bo_create_kernel = dm_test_bo_create_kernel,
+ .bo_free_kernel = dm_test_bo_free_kernel,
+};
+
static int dm_test_dmub_hw_access_init(struct kunit *test)
{
+ void *cpu_ptr;
+
+ cpu_ptr = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cpu_ptr);
+
+ dm_test_bo = (struct dm_test_bo_ctx) { .cpu_ptr = cpu_ptr };
dm_test_cgs = (struct dm_test_cgs_ctx) { .dev.ops = &dm_test_cgs_ops };
+ amdgpu_dm_services_kunit_set_ops(&dm_test_services_ops);
+
return 0;
}
+static void dm_test_dmub_hw_access_exit(struct kunit *test)
+{
+ amdgpu_dm_services_kunit_set_ops(NULL);
+}
+
+static struct amdgpu_device *dm_test_alloc_adev_with_cgs(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->dm.cgs_device = &dm_test_cgs.dev;
+ INIT_LIST_HEAD(&adev->dm.da_list);
+
+ return adev;
+}
+
/* Tests for amdgpu_dm_dmub_reg_read() and amdgpu_dm_dmub_reg_write() */
/**
@@ -1427,6 +1509,105 @@ static void dm_test_dmub_reg_write_then_read(struct kunit *test)
KUNIT_EXPECT_EQ(test, dm_test_cgs.last_read_offset, 0x1234U);
}
+/* Tests for dm_dmub_get_vbios_bounding_box() */
+
+/**
+ * dm_test_dmub_get_vbios_bounding_box_success - Test the full bounding box handshake
+ * @test: The KUnit test context
+ *
+ * Every ASIC with a bounding box allocates a non-zero GART buffer, then sends
+ * and gets an ack for the four address words and the copy request.
+ */
+static void dm_test_dmub_get_vbios_bounding_box_success(struct kunit *test)
+{
+ static const u32 ip_versions[] = {
+ IP_VERSION(4, 0, 1),
+ IP_VERSION(4, 2, 0),
+ IP_VERSION(4, 2, 1),
+ IP_VERSION(6, 0, 0),
+ };
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_cgs(test);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ip_versions); i++) {
+ void *bb;
+
+ adev->ip_versions[DCE_HWIP][0] = ip_versions[i];
+
+ bb = dm_dmub_get_vbios_bounding_box(adev);
+
+ KUNIT_EXPECT_PTR_EQ_MSG(test, bb, dm_test_bo.cpu_ptr,
+ "IP version 0x%08x", ip_versions[i]);
+ KUNIT_EXPECT_GT_MSG(test, dm_test_bo.create_size, 0UL,
+ "IP version 0x%08x", ip_versions[i]);
+ KUNIT_EXPECT_EQ(test, dm_test_bo.create_domain,
+ (u32)AMDGPU_GEM_DOMAIN_GTT);
+ KUNIT_EXPECT_EQ(test, dm_test_cgs.last_write_offset,
+ (unsigned int)DM_TEST_GPINT_REG);
+ /* Four address words plus the copy request per ASIC. */
+ KUNIT_EXPECT_EQ(test, dm_test_cgs.write_calls, 5 * (i + 1));
+
+ dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, bb);
+ }
+}
+
+/**
+ * dm_test_dmub_get_vbios_bounding_box_alloc_fails - Test the allocation failure path
+ * @test: The KUnit test context
+ *
+ * When the GPU memory allocation fails, no GPINT command is sent.
+ */
+static void dm_test_dmub_get_vbios_bounding_box_alloc_fails(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_cgs(test);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(6, 0, 0);
+ dm_test_bo.create_ret = -ENOMEM;
+
+ KUNIT_EXPECT_NULL(test, dm_dmub_get_vbios_bounding_box(adev));
+ KUNIT_EXPECT_EQ(test, dm_test_cgs.write_calls, 0U);
+}
+
+/**
+ * dm_test_dmub_get_vbios_bounding_box_addr_timeout - Test an unacked address word
+ * @test: The KUnit test context
+ *
+ * When the firmware never acks the first address word, the handshake stops
+ * there and the buffer is released.
+ */
+static void dm_test_dmub_get_vbios_bounding_box_addr_timeout(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_cgs(test);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(6, 0, 0);
+ dm_test_cgs.fail_on_write = 1;
+
+ KUNIT_EXPECT_NULL(test, dm_dmub_get_vbios_bounding_box(adev));
+ KUNIT_EXPECT_EQ(test, dm_test_cgs.write_calls, 1U);
+ KUNIT_EXPECT_EQ(test, dm_test_bo.free_calls, 1U);
+ KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.da_list));
+}
+
+/**
+ * dm_test_dmub_get_vbios_bounding_box_copy_timeout - Test an unacked copy request
+ * @test: The KUnit test context
+ *
+ * When all four address words are acked but the copy request is not, the
+ * buffer is released and no bounding box is returned.
+ */
+static void dm_test_dmub_get_vbios_bounding_box_copy_timeout(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_cgs(test);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(6, 0, 0);
+ dm_test_cgs.fail_on_write = 5;
+
+ KUNIT_EXPECT_NULL(test, dm_dmub_get_vbios_bounding_box(adev));
+ KUNIT_EXPECT_EQ(test, dm_test_cgs.write_calls, 5U);
+ KUNIT_EXPECT_EQ(test, dm_test_bo.free_calls, 1U);
+ KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.da_list));
+}
+
static struct kunit_case amdgpu_dm_dmub_tests[] = {
/* dm_register_dmub_notify_callback() */
KUNIT_CASE(dm_test_register_dmub_notify_callback_null_callback),
@@ -1495,12 +1676,18 @@ static struct kunit_suite amdgpu_dm_dmub_test_suite = {
static struct kunit_case amdgpu_dm_dmub_hw_access_tests[] = {
/* amdgpu_dm_dmub_reg_read() and amdgpu_dm_dmub_reg_write() */
KUNIT_CASE(dm_test_dmub_reg_write_then_read),
+ /* dm_dmub_get_vbios_bounding_box() */
+ KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_success),
+ KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_alloc_fails),
+ KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_addr_timeout),
+ KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_copy_timeout),
{}
};
static struct kunit_suite amdgpu_dm_dmub_hw_access_test_suite = {
.name = "amdgpu_dm_dmub_hw_access",
.init = dm_test_dmub_hw_access_init,
+ .exit = dm_test_dmub_hw_access_exit,
.test_cases = amdgpu_dm_dmub_hw_access_tests,
};
--
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.