tests/qtest/ufs-test: Add test code for MCQ functionality

This patch tests whether MCQ initialization and basic read-write
operations work correctly when the MCQ parameter of hw/ufs is enabled.

Acked-by: Fabiano Rosas <farosas@suse.de>
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
This commit is contained in:
Jeuk Kim 2025-02-12 14:04:21 +09:00 committed by Jeuk Kim
parent 5cb3566a58
commit a54596a960

View File

@ -15,6 +15,7 @@
#include "block/ufs.h"
#include "qemu/bitmap.h"
#define DWORD_BYTE 4
/* Test images sizes in Bytes */
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
/* Timeout for various operations, in seconds. */
@ -28,6 +29,10 @@
#define UTP_PRDT_UPIU_OFFSET 2048
#define UTRD_TEST_SLOT 0
#define UFS_MAX_CMD_DESC 32
/* Constants for MCQ */
#define TEST_QID 0
#define QUEUE_SIZE 32
#define UFS_MCQ_MAX_QNUM 32
typedef struct QUfs QUfs;
@ -36,12 +41,22 @@ struct QUfs {
QPCIDevice dev;
QPCIBar bar;
uint64_t utrlba;
DECLARE_BITMAP(cmd_desc_bitmap, UFS_MAX_CMD_DESC);
uint64_t cmd_desc_addr;
uint64_t data_buffer_addr;
bool enabled;
bool support_mcq;
/* for legacy doorbell mode */
uint64_t utrlba;
/* for mcq mode */
uint32_t maxq;
uint64_t sqlba[UFS_MCQ_MAX_QNUM];
uint64_t cqlba[UFS_MCQ_MAX_QNUM];
uint64_t sqdao[UFS_MCQ_MAX_QNUM];
uint64_t cqdao[UFS_MCQ_MAX_QNUM];
};
static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset)
@ -106,31 +121,67 @@ static UtpTransferReqDesc ufs_build_req_utrd(uint64_t command_desc_base_addr,
}
static enum UtpOcsCodes
ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun,
const UtpTransferReqDesc *utrd)
__ufs_send_transfer_request_doorbell(QUfs *ufs, uint8_t lun,
const UtpTransferReqDesc *utrd)
{
UtpTransferReqDesc utrd_result;
/*
* Currently, the transfer request is sent synchronously, so UTRD_TEST_SLOT
* is fixed to 0. If asynchronous testing is added in the future, this value
* should be adjusted dynamically.
*/
uint64_t utrd_addr =
ufs->utrlba + UTRD_TEST_SLOT * sizeof(UtpTransferReqDesc);
UtpTransferReqDesc utrd_result;
qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd));
/* Ring Doorbell */
/* Ring the doorbell */
ufs_wreg(ufs, A_UTRLDBR, 1);
ufs_wait_for_irq(ufs);
g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
/* Handle completed command */
qtest_memread(ufs->dev.bus->qts, utrd_addr, &utrd_result,
sizeof(utrd_result));
return le32_to_cpu(utrd_result.header.dword_2) & 0xf;
}
static enum UtpOcsCodes
__ufs_send_transfer_request_mcq(QUfs *ufs, uint8_t lun,
const UtpTransferReqDesc *utrd)
{
uint32_t sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4);
uint64_t utrd_addr = ufs->sqlba[TEST_QID] + sqtp;
uint32_t cqhp;
uint64_t cqentry_addr;
UfsCqEntry cqentry;
qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd));
/* Insert a new entry into the submission queue */
sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4);
sqtp = (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry));
ufs_wreg(ufs, ufs->sqdao[TEST_QID] + 0x4, sqtp);
ufs_wait_for_irq(ufs);
g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, CQES));
ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, CQES, 1));
/* Handle the completed command from the completion queue */
cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]);
cqentry_addr = ufs->cqlba[TEST_QID] + cqhp;
qtest_memread(ufs->dev.bus->qts, cqentry_addr, &cqentry, sizeof(cqentry));
ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp);
return cqentry.status;
}
static enum UtpOcsCodes
ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun,
const UtpTransferReqDesc *utrd)
{
if (ufs->support_mcq) {
return __ufs_send_transfer_request_mcq(ufs, lun, utrd);
}
return __ufs_send_transfer_request_doorbell(ufs, lun, utrd);
}
static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out)
{
int cmd_desc_slot = alloc_cmd_desc_slot(ufs);
@ -342,6 +393,10 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY));
g_assert_true(FIELD_EX32(hcs, HCS, UCRDY));
/* Check MCQ support */
cap = ufs_rreg(ufs, A_CAP);
ufs->support_mcq = FIELD_EX32(cap, CAP, MCQS);
/* Enable all interrupt functions */
ie = FIELD_DP32(ie, IE, UTRCE, 1);
ie = FIELD_DP32(ie, IE, UEE, 1);
@ -354,21 +409,66 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
ie = FIELD_DP32(ie, IE, HCFEE, 1);
ie = FIELD_DP32(ie, IE, SBFEE, 1);
ie = FIELD_DP32(ie, IE, CEFEE, 1);
if (ufs->support_mcq) {
ie = FIELD_DP32(ie, IE, CQEE, 1);
}
ufs_wreg(ufs, A_IE, ie);
ufs_wreg(ufs, A_UTRIACR, 0);
/* Enable transfer request */
cap = ufs_rreg(ufs, A_CAP);
nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1;
ufs->cmd_desc_addr =
guest_alloc(alloc, UFS_MAX_CMD_DESC * UTP_COMMAND_DESCRIPTOR_SIZE);
ufs->data_buffer_addr =
guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc));
ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff);
ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32);
ufs_wreg(ufs, A_UTRLRSR, 1);
if (ufs->support_mcq) {
uint32_t mcqcap, qid, qcfgptr, mcq_reg_offset;
uint32_t cqattr = 0, sqattr = 0;
mcqcap = ufs_rreg(ufs, A_MCQCAP);
qcfgptr = FIELD_EX32(mcqcap, MCQCAP, QCFGPTR);
ufs->maxq = FIELD_EX32(mcqcap, MCQCAP, MAXQ) + 1;
for (qid = 0; qid < ufs->maxq; ++qid) {
ufs->sqlba[qid] =
guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc));
ufs->cqlba[qid] =
guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc));
mcq_reg_offset = qcfgptr * 0x200 + qid * 0x40;
ufs_wreg(ufs, mcq_reg_offset + A_SQLBA,
ufs->sqlba[qid] & 0xffffffff);
ufs_wreg(ufs, mcq_reg_offset + A_SQUBA, ufs->sqlba[qid] >> 32);
ufs_wreg(ufs, mcq_reg_offset + A_CQLBA,
ufs->cqlba[qid] & 0xffffffff);
ufs_wreg(ufs, mcq_reg_offset + A_CQUBA, ufs->cqlba[qid] >> 32);
/* Enable Completion Queue */
cqattr = FIELD_DP32(cqattr, CQATTR, CQEN, 1);
cqattr = FIELD_DP32(cqattr, CQATTR, SIZE,
QUEUE_SIZE * sizeof(UtpTransferReqDesc) /
DWORD_BYTE);
ufs_wreg(ufs, mcq_reg_offset + A_CQATTR, cqattr);
/* Enable Submission Queue */
sqattr = FIELD_DP32(sqattr, SQATTR, SQEN, 1);
sqattr = FIELD_DP32(sqattr, SQATTR, SIZE,
QUEUE_SIZE * sizeof(UtpTransferReqDesc) /
DWORD_BYTE);
sqattr = FIELD_DP32(sqattr, SQATTR, CQID, qid);
ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, sqattr);
/* Cache head & tail pointer */
ufs->sqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_SQDAO);
ufs->cqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_CQDAO);
}
} else {
nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1;
ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc));
ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff);
ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32);
ufs_wreg(ufs, A_UTRLRSR, 1);
}
/* Send nop out to test transfer request */
ocs = ufs_send_nop_out(ufs, &rsp_upiu);
@ -402,7 +502,15 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc)
{
if (ufs->enabled) {
guest_free(alloc, ufs->utrlba);
if (ufs->support_mcq) {
for (uint32_t qid = 0; qid < ufs->maxq; ++qid) {
guest_free(alloc, ufs->sqlba[qid]);
guest_free(alloc, ufs->cqlba[qid]);
}
} else {
guest_free(alloc, ufs->utrlba);
}
guest_free(alloc, ufs->cmd_desc_addr);
guest_free(alloc, ufs->data_buffer_addr);
}
@ -966,12 +1074,16 @@ static void ufs_register_nodes(void)
QOSGraphEdgeOptions edge_opts = {
.before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on",
.after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0",
.extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8"
.extra_device_opts = "addr=04.0,id=ufs0"
};
QOSGraphTestOptions io_test_opts = {
.before = ufs_blk_test_setup,
};
QOSGraphTestOptions io_test_opts = { .before = ufs_blk_test_setup,
.edge.extra_device_opts =
"mcq=false,nutrs=32,nutmrs=8" };
QOSGraphTestOptions mcq_test_opts = { .before = ufs_blk_test_setup,
.edge.extra_device_opts =
"mcq=true,mcq-maxq=1" };
add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) });
@ -991,13 +1103,14 @@ static void ufs_register_nodes(void)
return;
}
qos_add_test("init", "ufs", ufstest_init, NULL);
qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts);
qos_add_test("flag read-write", "ufs",
ufstest_query_flag_request, &io_test_opts);
qos_add_test("attr read-write", "ufs",
ufstest_query_attr_request, &io_test_opts);
qos_add_test("desc read-write", "ufs",
ufstest_query_desc_request, &io_test_opts);
qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts);
qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts);
qos_add_test("query-flag", "ufs", ufstest_query_flag_request,
&io_test_opts);
qos_add_test("query-attribute", "ufs", ufstest_query_attr_request,
&io_test_opts);
qos_add_test("query-desciptor", "ufs", ufstest_query_desc_request,
&io_test_opts);
}
libqos_init(ufs_register_nodes);