drm/amd/display: Add KUnit tests for link_lock and psp SRM helpers

Cover link_lock() across all links (lock then unlock) and the
max_link == 0 no-op path, checking each per-link mutex ends in
the expected state.

Also cover the psp_get_srm() and psp_set_srm() guard paths when
the HDCP TA context is uninitialized: psp_get_srm() returns NULL
and psp_set_srm() returns -EINVAL, both leaving their output
parameters untouched. The post-guard paths invoke real PSP
firmware and are not unit-testable in UML.

Reviewed-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Bhawanpreet Lakha
2026-07-10 14:05:41 -04:00
committed by Alex Deucher
parent f73dd04acd
commit fad644eab1
3 changed files with 140 additions and 3 deletions

View File

@@ -106,7 +106,8 @@ static bool lp_atomic_write_poll_read_aux(
return dm_atomic_write_poll_read_aux(link, write, poll, read, poll_timeout_us, poll_mask_msb);
}
static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size)
STATIC_IFN_KUNIT
uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size)
{
struct ta_hdcp_shared_memory *hdcp_cmd;
@@ -129,8 +130,10 @@ static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint
return hdcp_cmd->out_msg.hdcp_get_srm.srm_buf;
}
EXPORT_IF_KUNIT(psp_get_srm);
static int psp_set_srm(struct psp_context *psp,
STATIC_IFN_KUNIT
int psp_set_srm(struct psp_context *psp,
u8 *srm, uint32_t srm_size, uint32_t *srm_version)
{
struct ta_hdcp_shared_memory *hdcp_cmd;
@@ -157,6 +160,7 @@ static int psp_set_srm(struct psp_context *psp,
*srm_version = hdcp_cmd->out_msg.hdcp_set_srm.srm_version;
return 0;
}
EXPORT_IF_KUNIT(psp_set_srm);
STATIC_IFN_KUNIT
void process_output(struct hdcp_workqueue *hdcp_work)
@@ -245,7 +249,8 @@ void hdcp_get_link_display_adjustments(
}
EXPORT_IF_KUNIT(hdcp_get_link_display_adjustments);
static void link_lock(struct hdcp_workqueue *work, bool lock)
STATIC_IFN_KUNIT
void link_lock(struct hdcp_workqueue *work, bool lock)
{
int i = 0;
@@ -256,6 +261,7 @@ static void link_lock(struct hdcp_workqueue *work, bool lock)
mutex_unlock(&work[i].mutex);
}
}
EXPORT_IF_KUNIT(link_lock);
STATIC_IFN_KUNIT
void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work,

View File

@@ -44,6 +44,7 @@ struct mod_hdcp_link;
struct mod_hdcp_display;
struct cp_psp;
struct amdgpu_device;
struct psp_context;
struct hdcp_workqueue {
struct work_struct cpirq_work;
@@ -113,6 +114,9 @@ void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work,
unsigned int conn_index,
bool enable_encryption);
void event_property_update(struct work_struct *work);
void link_lock(struct hdcp_workqueue *work, bool lock);
uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size);
int psp_set_srm(struct psp_context *psp, u8 *srm, uint32_t srm_size, uint32_t *srm_version);
#endif
#endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */

View File

@@ -8,6 +8,7 @@
#include <kunit/test.h>
#include <linux/workqueue.h>
#include "amdgpu.h"
#include "amdgpu_dm_hdcp.h"
static void dummy_work_fn(struct work_struct *work) {}
@@ -632,6 +633,126 @@ static void dm_test_hdcp_create_workqueue_zero_max_links_returns_null(struct kun
/* End of tests for hdcp_create_workqueue() */
/* Tests for link_lock() */
/**
* dm_test_link_lock_locks_and_unlocks_all_links - lock/unlock spans every link
* @test: KUnit test context
*
* link_lock() should acquire the mutex of every entry from 0 to max_link
* when locking, and release all of them when unlocking. A subsequent
* lock/unlock cycle must succeed, proving the mutexes were left released.
*/
static void dm_test_link_lock_locks_and_unlocks_all_links(struct kunit *test)
{
const int num_links = 3;
struct hdcp_workqueue *work;
int i;
work = kunit_kcalloc(test, num_links, sizeof(*work), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, work);
/* max_link is read from the first element. */
work[0].max_link = num_links;
for (i = 0; i < num_links; i++)
mutex_init(&work[i].mutex);
link_lock(work, true);
for (i = 0; i < num_links; i++)
KUNIT_EXPECT_TRUE(test, mutex_is_locked(&work[i].mutex));
link_lock(work, false);
for (i = 0; i < num_links; i++)
KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work[i].mutex));
/* Mutexes must be re-acquirable after being released. */
link_lock(work, true);
for (i = 0; i < num_links; i++)
KUNIT_EXPECT_TRUE(test, mutex_is_locked(&work[i].mutex));
link_lock(work, false);
}
/**
* dm_test_link_lock_zero_links_is_noop - zero max_link touches no mutexes
* @test: KUnit test context
*
* When max_link is zero, link_lock() must not touch any mutex and simply
* return, leaving the (single) entry's mutex unlocked.
*/
static void dm_test_link_lock_zero_links_is_noop(struct kunit *test)
{
struct hdcp_workqueue *work;
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, work);
mutex_init(&work->mutex);
work->max_link = 0;
link_lock(work, true);
KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work->mutex));
}
/* End of tests for link_lock() */
/* Tests for psp_get_srm() and psp_set_srm() */
/**
* dm_test_psp_get_srm_uninitialized_returns_null - GET fails when TA not initialized
* @test: KUnit test context
*
* When the HDCP TA context is not initialized, psp_get_srm() must take the
* guard path and return NULL without touching the output parameters or
* invoking the (real) firmware path.
*/
static void dm_test_psp_get_srm_uninitialized_returns_null(struct kunit *test)
{
struct psp_context *psp;
uint32_t srm_version = 0xdead;
uint32_t srm_size = 0xbeef;
uint8_t *srm;
psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, psp);
/* kzalloc leaves hdcp_context.context.initialized == false */
srm = psp_get_srm(psp, &srm_version, &srm_size);
KUNIT_EXPECT_PTR_EQ(test, srm, NULL);
/* Output parameters must be left untouched on the guard path. */
KUNIT_EXPECT_EQ(test, srm_version, 0xdead);
KUNIT_EXPECT_EQ(test, srm_size, 0xbeef);
}
/**
* dm_test_psp_set_srm_uninitialized_returns_einval - SET fails when TA not initialized
* @test: KUnit test context
*
* When the HDCP TA context is not initialized, psp_set_srm() must take the
* guard path and return -EINVAL without updating srm_version or invoking
* the (real) firmware path.
*/
static void dm_test_psp_set_srm_uninitialized_returns_einval(struct kunit *test)
{
struct psp_context *psp;
uint32_t srm_version = 0xdead;
u8 srm_buf[4] = {0};
int ret;
psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, psp);
/* kzalloc leaves hdcp_context.context.initialized == false */
ret = psp_set_srm(psp, srm_buf, sizeof(srm_buf), &srm_version);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
/* srm_version must be left untouched on the guard path. */
KUNIT_EXPECT_EQ(test, srm_version, 0xdead);
}
/* End of tests for psp_get_srm() and psp_set_srm() */
static struct kunit_case dm_hdcp_test_cases[] = {
/* hdcp_get_content_protection_from_status() */
KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired),
@@ -663,6 +784,12 @@ static struct kunit_case dm_hdcp_test_cases[] = {
KUNIT_CASE(dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate),
/* hdcp_create_workqueue() */
KUNIT_CASE(dm_test_hdcp_create_workqueue_zero_max_links_returns_null),
/* link_lock() */
KUNIT_CASE(dm_test_link_lock_locks_and_unlocks_all_links),
KUNIT_CASE(dm_test_link_lock_zero_links_is_noop),
/* psp_get_srm() / psp_set_srm() */
KUNIT_CASE(dm_test_psp_get_srm_uninitialized_returns_null),
KUNIT_CASE(dm_test_psp_set_srm_uninitialized_returns_einval),
{}
};