Re: [PULL v2 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
On 6/8/2024 12:02 AM, Peter Maydell wrote: On Mon, 3 Jun 2024 at 09:38, Jeuk Kim wrote: From: Minwoo Im This patch adds support for MCQ defined in UFSHCI 4.0. This patch utilized the legacy I/O codes as much as possible to support MCQ. MCQ operation & runtime register is placed at 0x1000 offset of UFSHCI register statically with no spare space among four registers (48B): UfsMcqSqReg, UfsMcqSqIntReg, UfsMcqCqReg, UfsMcqCqIntReg The maxinum number of queue is 32 as per spec, and the default MAC(Multiple Active Commands) are 32 in the device. Example: -device ufs,serial=foo,id=ufs0,mcq=true,mcq-maxq=8 Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-3-minwoo...@samsung.com> Signed-off-by: Jeuk Kim Hi; Coverity reported a potential issue with this code. I don't think it's an actual bug, but it would be nice to clean it up and keep Coverity happy. (CID 1546866). static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size) { UfsHc *u = (UfsHc *)opaque; -uint8_t *ptr = (uint8_t *)>reg; +uint8_t *ptr; uint64_t value; - -if (addr > sizeof(u->reg) - size) { Before this change, we checked addr against (sizeof(u->reg) - size). +uint64_t offset; + +if (addr < sizeof(u->reg)) { Now we changed to check it against sizeof(u->reg). That means Coverity thinks it's possible that we could have addr = sizeof(u->reg) - 1... +offset = addr; +ptr = (uint8_t *)>reg; +} else if (ufs_is_mcq_reg(u, addr)) { +offset = addr - ufs_mcq_reg_addr(u, 0); +ptr = (uint8_t *)>mcq_reg; +} else if (ufs_is_mcq_op_reg(u, addr)) { +offset = addr - ufs_mcq_op_reg_addr(u, 0); +ptr = (uint8_t *)>mcq_op_reg; +} else { trace_ufs_err_invalid_register_offset(addr); return 0; } -value = *(uint32_t *)(ptr + addr); +value = *(uint32_t *)(ptr + offset); ...so Coverity thinks that this write of a 32-bit value might overrun the end of the array. trace_ufs_mmio_read(addr, value, size); return value; Side note: why use uint8_t* for the type of "ptr" in these functions? We know it must be a uint32_t* (it comes either from the u->reg or from one of these u_mcq_reg or u->mcq_op_reg fields, and they must all be uint32_t), and using the right type would reduce the number of casts you need to do. This is probably to make the offset calculation easier (since we can write `addr + offset` instead of `addr + offset/4`). But I agree with you that it can be semantically confusing. I'll fix it. It also helps the reader a little, because using a uint8_t implies that you're indexing into an array-of-bytes, and if you were doing that it would be a bug (because both of it not handling endianness correctly and because of it not handling alignment correctly). } @@ -423,13 +802,17 @@ static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data, { UfsHc *u = (UfsHc *)opaque; -if (addr > sizeof(u->reg) - size) { +trace_ufs_mmio_write(addr, data, size); + +if (addr < sizeof(u->reg)) { Similarly here we changed the bounds check we were doing. +ufs_write_reg(u, addr, data, size); +} else if (ufs_is_mcq_reg(u, addr)) { +ufs_write_mcq_reg(u, addr - ufs_mcq_reg_addr(u, 0), data, size); +} else if (ufs_is_mcq_op_reg(u, addr)) { +ufs_write_mcq_op_reg(u, addr - ufs_mcq_op_reg_addr(u, 0), data, size); +} else { trace_ufs_err_invalid_register_offset(addr); -return; } - -trace_ufs_mmio_write(addr, data, size); -ufs_write_reg(u, addr, data, size); thanks -- PMM Thanks for the detailed explanation! I will prepare a patch to fix the coverity issue. Thanks, Jeuk
[PULL v2 1/2] hw/ufs: Update MCQ-related fields to block/ufs.h
From: Minwoo Im This patch is a prep patch for the following MCQ support patch for hw/ufs. This patch updated minimal mandatory fields to support MCQ based on UFSHCI 4.0. Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-2-minwoo...@samsung.com> Signed-off-by: Jeuk Kim --- include/block/ufs.h | 108 +++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/include/block/ufs.h b/include/block/ufs.h index d61598b8f3..3513b6e772 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -7,7 +7,7 @@ typedef struct QEMU_PACKED UfsReg { uint32_t cap; -uint32_t rsvd0; +uint32_t mcqcap; uint32_t ver; uint32_t rsvd1; uint32_t hcpid; @@ -46,6 +46,13 @@ typedef struct QEMU_PACKED UfsReg { uint32_t rsvd7[4]; uint32_t rsvd8[16]; uint32_t ccap; +uint32_t rsvd9[127]; +uint32_t config; +uint32_t rsvd10[3]; +uint32_t rsvd11[28]; +uint32_t mcqconfig; +uint32_t esilba; +uint32_t esiuba; } UfsReg; REG32(CAP, offsetof(UfsReg, cap)) @@ -57,6 +64,15 @@ REG32(CAP, offsetof(UfsReg, cap)) FIELD(CAP, OODDS, 25, 1) FIELD(CAP, UICDMETMS, 26, 1) FIELD(CAP, CS, 28, 1) +FIELD(CAP, LSDBS, 29, 1) +FIELD(CAP, MCQS, 30, 1) +REG32(MCQCAP, offsetof(UfsReg, mcqcap)) +FIELD(MCQCAP, MAXQ, 0, 8) +FIELD(MCQCAP, SP, 8, 1) +FIELD(MCQCAP, RRP, 9, 1) +FIELD(MCQCAP, EIS, 10, 1) +FIELD(MCQCAP, QCFGPTR, 16, 8) +FIELD(MCQCAP, MIAG, 24, 8) REG32(VER, offsetof(UfsReg, ver)) REG32(HCPID, offsetof(UfsReg, hcpid)) REG32(HCMID, offsetof(UfsReg, hcmid)) @@ -78,6 +94,7 @@ REG32(IS, offsetof(UfsReg, is)) FIELD(IS, HCFES, 16, 1) FIELD(IS, SBFES, 17, 1) FIELD(IS, CEFES, 18, 1) +FIELD(IS, CQES, 20, 1) REG32(IE, offsetof(UfsReg, ie)) FIELD(IE, UTRCE, 0, 1) FIELD(IE, UDEPRIE, 1, 1) @@ -95,6 +112,7 @@ REG32(IE, offsetof(UfsReg, ie)) FIELD(IE, HCFEE, 16, 1) FIELD(IE, SBFEE, 17, 1) FIELD(IE, CEFEE, 18, 1) +FIELD(IE, CQEE, 20, 1) REG32(HCS, offsetof(UfsReg, hcs)) FIELD(HCS, DP, 0, 1) FIELD(HCS, UTRLRDY, 1, 1) @@ -128,6 +146,10 @@ REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1)) REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2)) REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3)) REG32(CCAP, offsetof(UfsReg, ccap)) +REG32(CONFIG, offsetof(UfsReg, config)) +FIELD(CONFIG, QT, 0, 1) +REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig)) +FIELD(MCQCONFIG, MAC, 8, 8) #define UFS_INTR_MASK\ ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ @@ -157,6 +179,69 @@ REG32(CCAP, offsetof(UfsReg, ccap)) ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \ UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK) +typedef struct QEMU_PACKED UfsMcqReg { +uint32_t sqattr; +uint32_t sqlba; +uint32_t squba; +uint32_t sqdao; +uint32_t sqisao; +uint32_t sqcfg; +uint32_t rsvd0[2]; +uint32_t cqattr; +uint32_t cqlba; +uint32_t cquba; +uint32_t cqdao; +uint32_t cqisao; +uint32_t cqcfg; +uint32_t rsvd1[2]; +} UfsMcqReg; + +REG32(SQATTR, offsetof(UfsMcqReg, sqattr)) +FIELD(SQATTR, SIZE, 0, 16) +FIELD(SQATTR, CQID, 16, 8) +FIELD(SQATTR, SQPL, 28, 3) +FIELD(SQATTR, SQEN, 31, 1) +REG32(SQLBA, offsetof(UfsMcqReg, sqlba)) +REG32(SQUBA, offsetof(UfsMcqReg, squba)) +REG32(SQDAO, offsetof(UfsMcqReg, sqdao)) +REG32(SQISAO, offsetof(UfsMcqReg, sqisao)) +REG32(SQCFG, offsetof(UfsMcqReg, sqcfg)) +REG32(CQATTR, offsetof(UfsMcqReg, cqattr)) +FIELD(CQATTR, SIZE, 0, 16) +FIELD(CQATTR, CQEN, 31, 1) +REG32(CQLBA, offsetof(UfsMcqReg, cqlba)) +REG32(CQUBA, offsetof(UfsMcqReg, cquba)) +REG32(CQDAO, offsetof(UfsMcqReg, cqdao)) +REG32(CQISAO, offsetof(UfsMcqReg, cqisao)) +REG32(CQCFG, offsetof(UfsMcqReg, cqcfg)) + +typedef struct QEMU_PACKED UfsMcqSqReg { +uint32_t hp; +uint32_t tp; +uint32_t rtc; +uint32_t cti; +uint32_t rts; +} UfsMcqSqReg; + +typedef struct QEMU_PACKED UfsMcqCqReg { +uint32_t hp; +uint32_t tp; +} UfsMcqCqReg; + +typedef struct QEMU_PACKED UfsMcqSqIntReg { +uint32_t is; +uint32_t ie; +} UfsMcqSqIntReg; + +typedef struct QEMU_PACKED UfsMcqCqIntReg { +uint32_t is; +uint32_t ie; +uint32_t iacr; +} UfsMcqCqIntReg; + +REG32(CQIS, offsetof(UfsMcqCqIntReg, is)) +FIELD(CQIS, TEPS, 0, 1) + typedef struct QEMU_PACKED DeviceDescriptor { uint8_t length; uint8_t descriptor_idn; @@ -1064,9 +1149,26 @@ typedef struct QEMU_PACKED UtpUpiuRsp { }; } UtpUpiuRsp; +/* + * MCQ Completion Queue Entry + */ +typedef UtpTransferReqDesc UfsSqEntry; +typedef struct QEMU_PACKED UfsCqEntry { +uint64_t utp_addr; +uint16_t resp_len; +uint16_t resp_off; +uint16_t prdt_len; +uint16_t prdt_off; +uint8_t status; +uint8_t error; +uint16_t rsvd1; +uint32_t rsvd2[3]; +} UfsCqEntry; + static inli
[PULL v2 0/2] ufs queue
From: Jeuk Kim The following changes since commit 74abb45dac6979e7ff76172b7f0a24e869405184: Merge tag 'pull-target-arm-20240531' of https://git.linaro.org/people/pmaydell/qemu-arm into staging (2024-05-31 11:10:10 -0700) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20240603 for you to fetch changes up to 5c079578d2e46df626d13eeb629c7d761a5c4e44: hw/ufs: Add support MCQ of UFSHCI 4.0 (2024-06-03 16:20:42 +0900) hw/ufs patches - Add support MCQ of UFSHCI 4.0 Minwoo Im (2): hw/ufs: Update MCQ-related fields to block/ufs.h hw/ufs: Add support MCQ of UFSHCI 4.0 hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c| 475 ++-- hw/ufs/ufs.h| 98 ++- include/block/ufs.h | 131 ++- 4 files changed, 699 insertions(+), 22 deletions(-)
[PULL v2 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
From: Minwoo Im This patch adds support for MCQ defined in UFSHCI 4.0. This patch utilized the legacy I/O codes as much as possible to support MCQ. MCQ operation & runtime register is placed at 0x1000 offset of UFSHCI register statically with no spare space among four registers (48B): UfsMcqSqReg, UfsMcqSqIntReg, UfsMcqCqReg, UfsMcqCqIntReg The maxinum number of queue is 32 as per spec, and the default MAC(Multiple Active Commands) are 32 in the device. Example: -device ufs,serial=foo,id=ufs0,mcq=true,mcq-maxq=8 Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-3-minwoo...@samsung.com> Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c| 475 ++-- hw/ufs/ufs.h| 98 - include/block/ufs.h | 23 ++- 4 files changed, 593 insertions(+), 20 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 665e1a942b..531dcfc686 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -11,13 +11,18 @@ ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" +ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8"" +ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" +ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" # error condition ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_read_sq(uint8_t sqid, uint64_t addr) "failed to read sq entry. sqid %"PRIu8", hwaddr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_dma_write_cq(uint8_t cqid, uint64_t addr) "failed to write cq entry. cqid %"PRIu8", hwaddr %"PRIu64"" ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" @@ -31,3 +36,15 @@ ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8"" ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8"" ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8"" +ufs_err_mcq_db_wr_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_db_wr_invalid_db(uint8_t qid, uint32_t db) "invalid mcq doorbell sqid %"PRIu8", db %"PRIu32"" +ufs_err_mcq_create_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_create_sq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_sq_already_exists(uint8_t qid) "mcq sqid %"PRIu8 "already exists" +ufs_err_mcq_delete_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_delete_sq_not_exists(uint8_t qid) "mcq sqid %"PRIu8 "not exists" +ufs_err_mcq_create_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_cq_already_exists(uint8_t qid
Re: [PULL 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
On 5/29/2024 2:06 AM, Richard Henderson wrote: On 5/27/24 23:12, Jeuk Kim wrote: From: Minwoo Im This patch adds support for MCQ defined in UFSHCI 4.0. This patch utilized the legacy I/O codes as much as possible to support MCQ. MCQ operation & runtime register is placed at 0x1000 offset of UFSHCI register statically with no spare space among four registers (48B): UfsMcqSqReg, UfsMcqSqIntReg, UfsMcqCqReg, UfsMcqCqIntReg The maxinum number of queue is 32 as per spec, and the default MAC(Multiple Active Commands) are 32 in the device. Example: -device ufs,serial=foo,id=ufs0,mcq=true,mcq-maxq=8 Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-3-minwoo...@samsung.com> Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c | 475 ++-- hw/ufs/ufs.h | 98 - include/block/ufs.h | 23 ++- 4 files changed, 593 insertions(+), 20 deletions(-) Fails build: https://gitlab.com/qemu-project/qemu/-/jobs/6960270722 In file included from trace/trace-hw_ufs.c:5: ../hw/ufs/trace-events:28:24: error: format specifies type 'unsigned char' but the argument has type 'uint32_t' (aka 'unsigned int') [-Werror,-Wformat] , cqid, addr); ^~~~ ../hw/ufs/trace-events:25:112: error: format specifies type 'unsigned char' but the argument has type 'uint32_t' (aka 'unsigned int') [-Werror,-Wformat] qemu_log("ufs_err_dma_write_cq " "failed to write cq entry. cqid %"PRIu8", hwaddr %"PRIu64"" "\n", cqid, addr); ~~~ ^~~~ 2 errors generated. r~ Sorry about that. I'll fix it and send it back to you.
[PULL 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
From: Minwoo Im This patch adds support for MCQ defined in UFSHCI 4.0. This patch utilized the legacy I/O codes as much as possible to support MCQ. MCQ operation & runtime register is placed at 0x1000 offset of UFSHCI register statically with no spare space among four registers (48B): UfsMcqSqReg, UfsMcqSqIntReg, UfsMcqCqReg, UfsMcqCqIntReg The maxinum number of queue is 32 as per spec, and the default MAC(Multiple Active Commands) are 32 in the device. Example: -device ufs,serial=foo,id=ufs0,mcq=true,mcq-maxq=8 Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-3-minwoo...@samsung.com> Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c| 475 ++-- hw/ufs/ufs.h| 98 - include/block/ufs.h | 23 ++- 4 files changed, 593 insertions(+), 20 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 665e1a942b..dda7f8a2e5 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -11,13 +11,18 @@ ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" +ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8"" +ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" +ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" # error condition ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_read_sq(uint8_t qid, uint64_t addr) "failed to read sqe. SQ qid %"PRIu8", sqe addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_dma_write_cq(uint32_t cqid, uint64_t addr) "failed to write cq entry. cqid %"PRIu8", hwaddr %"PRIu64"" ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" @@ -31,3 +36,15 @@ ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8"" ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8"" ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8"" +ufs_err_mcq_db_wr_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_db_wr_invalid_db(uint8_t qid, uint32_t db) "invalid mcq doorbell sqid %"PRIu8", db %"PRIu32"" +ufs_err_mcq_create_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_create_sq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_sq_already_exists(uint8_t qid) "mcq sqid %"PRIu8 "already exists" +ufs_err_mcq_delete_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_delete_sq_not_exists(uint8_t qid) "mcq sqid %"PRIu8 "not exists" +ufs_err_mcq_create_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_cq_already_exists(uint8_t qid
[PULL 1/2] hw/ufs: Update MCQ-related fields to block/ufs.h
From: Minwoo Im This patch is a prep patch for the following MCQ support patch for hw/ufs. This patch updated minimal mandatory fields to support MCQ based on UFSHCI 4.0. Signed-off-by: Minwoo Im Reviewed-by: Jeuk Kim Message-Id: <20240528023106.856777-2-minwoo...@samsung.com> Signed-off-by: Jeuk Kim --- include/block/ufs.h | 108 +++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/include/block/ufs.h b/include/block/ufs.h index d61598b8f3..3513b6e772 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -7,7 +7,7 @@ typedef struct QEMU_PACKED UfsReg { uint32_t cap; -uint32_t rsvd0; +uint32_t mcqcap; uint32_t ver; uint32_t rsvd1; uint32_t hcpid; @@ -46,6 +46,13 @@ typedef struct QEMU_PACKED UfsReg { uint32_t rsvd7[4]; uint32_t rsvd8[16]; uint32_t ccap; +uint32_t rsvd9[127]; +uint32_t config; +uint32_t rsvd10[3]; +uint32_t rsvd11[28]; +uint32_t mcqconfig; +uint32_t esilba; +uint32_t esiuba; } UfsReg; REG32(CAP, offsetof(UfsReg, cap)) @@ -57,6 +64,15 @@ REG32(CAP, offsetof(UfsReg, cap)) FIELD(CAP, OODDS, 25, 1) FIELD(CAP, UICDMETMS, 26, 1) FIELD(CAP, CS, 28, 1) +FIELD(CAP, LSDBS, 29, 1) +FIELD(CAP, MCQS, 30, 1) +REG32(MCQCAP, offsetof(UfsReg, mcqcap)) +FIELD(MCQCAP, MAXQ, 0, 8) +FIELD(MCQCAP, SP, 8, 1) +FIELD(MCQCAP, RRP, 9, 1) +FIELD(MCQCAP, EIS, 10, 1) +FIELD(MCQCAP, QCFGPTR, 16, 8) +FIELD(MCQCAP, MIAG, 24, 8) REG32(VER, offsetof(UfsReg, ver)) REG32(HCPID, offsetof(UfsReg, hcpid)) REG32(HCMID, offsetof(UfsReg, hcmid)) @@ -78,6 +94,7 @@ REG32(IS, offsetof(UfsReg, is)) FIELD(IS, HCFES, 16, 1) FIELD(IS, SBFES, 17, 1) FIELD(IS, CEFES, 18, 1) +FIELD(IS, CQES, 20, 1) REG32(IE, offsetof(UfsReg, ie)) FIELD(IE, UTRCE, 0, 1) FIELD(IE, UDEPRIE, 1, 1) @@ -95,6 +112,7 @@ REG32(IE, offsetof(UfsReg, ie)) FIELD(IE, HCFEE, 16, 1) FIELD(IE, SBFEE, 17, 1) FIELD(IE, CEFEE, 18, 1) +FIELD(IE, CQEE, 20, 1) REG32(HCS, offsetof(UfsReg, hcs)) FIELD(HCS, DP, 0, 1) FIELD(HCS, UTRLRDY, 1, 1) @@ -128,6 +146,10 @@ REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1)) REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2)) REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3)) REG32(CCAP, offsetof(UfsReg, ccap)) +REG32(CONFIG, offsetof(UfsReg, config)) +FIELD(CONFIG, QT, 0, 1) +REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig)) +FIELD(MCQCONFIG, MAC, 8, 8) #define UFS_INTR_MASK\ ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ @@ -157,6 +179,69 @@ REG32(CCAP, offsetof(UfsReg, ccap)) ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \ UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK) +typedef struct QEMU_PACKED UfsMcqReg { +uint32_t sqattr; +uint32_t sqlba; +uint32_t squba; +uint32_t sqdao; +uint32_t sqisao; +uint32_t sqcfg; +uint32_t rsvd0[2]; +uint32_t cqattr; +uint32_t cqlba; +uint32_t cquba; +uint32_t cqdao; +uint32_t cqisao; +uint32_t cqcfg; +uint32_t rsvd1[2]; +} UfsMcqReg; + +REG32(SQATTR, offsetof(UfsMcqReg, sqattr)) +FIELD(SQATTR, SIZE, 0, 16) +FIELD(SQATTR, CQID, 16, 8) +FIELD(SQATTR, SQPL, 28, 3) +FIELD(SQATTR, SQEN, 31, 1) +REG32(SQLBA, offsetof(UfsMcqReg, sqlba)) +REG32(SQUBA, offsetof(UfsMcqReg, squba)) +REG32(SQDAO, offsetof(UfsMcqReg, sqdao)) +REG32(SQISAO, offsetof(UfsMcqReg, sqisao)) +REG32(SQCFG, offsetof(UfsMcqReg, sqcfg)) +REG32(CQATTR, offsetof(UfsMcqReg, cqattr)) +FIELD(CQATTR, SIZE, 0, 16) +FIELD(CQATTR, CQEN, 31, 1) +REG32(CQLBA, offsetof(UfsMcqReg, cqlba)) +REG32(CQUBA, offsetof(UfsMcqReg, cquba)) +REG32(CQDAO, offsetof(UfsMcqReg, cqdao)) +REG32(CQISAO, offsetof(UfsMcqReg, cqisao)) +REG32(CQCFG, offsetof(UfsMcqReg, cqcfg)) + +typedef struct QEMU_PACKED UfsMcqSqReg { +uint32_t hp; +uint32_t tp; +uint32_t rtc; +uint32_t cti; +uint32_t rts; +} UfsMcqSqReg; + +typedef struct QEMU_PACKED UfsMcqCqReg { +uint32_t hp; +uint32_t tp; +} UfsMcqCqReg; + +typedef struct QEMU_PACKED UfsMcqSqIntReg { +uint32_t is; +uint32_t ie; +} UfsMcqSqIntReg; + +typedef struct QEMU_PACKED UfsMcqCqIntReg { +uint32_t is; +uint32_t ie; +uint32_t iacr; +} UfsMcqCqIntReg; + +REG32(CQIS, offsetof(UfsMcqCqIntReg, is)) +FIELD(CQIS, TEPS, 0, 1) + typedef struct QEMU_PACKED DeviceDescriptor { uint8_t length; uint8_t descriptor_idn; @@ -1064,9 +1149,26 @@ typedef struct QEMU_PACKED UtpUpiuRsp { }; } UtpUpiuRsp; +/* + * MCQ Completion Queue Entry + */ +typedef UtpTransferReqDesc UfsSqEntry; +typedef struct QEMU_PACKED UfsCqEntry { +uint64_t utp_addr; +uint16_t resp_len; +uint16_t resp_off; +uint16_t prdt_len; +uint16_t prdt_off; +uint8_t status; +uint8_t error; +uint16_t rsvd1; +uint32_t rsvd2[3]; +} UfsCqEntry; + static inli
[PULL 0/2] ufs queue
From: Jeuk Kim The following changes since commit ad10b4badc1dd5b28305f9b9f1168cf0aa3ae946: Merge tag 'pull-error-2024-05-27' of https://repo.or.cz/qemu/armbru into staging (2024-05-27 06:40:42 -0700) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20240528 for you to fetch changes up to 71a82d3f0555e65c98df129ce0e38b2aa5681ec0: hw/ufs: Add support MCQ of UFSHCI 4.0 (2024-05-28 14:42:32 +0900) Add support MCQ of UFSHCI 4.0 Minwoo Im (2): hw/ufs: Update MCQ-related fields to block/ufs.h hw/ufs: Add support MCQ of UFSHCI 4.0 hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c| 475 ++-- hw/ufs/ufs.h| 98 ++- include/block/ufs.h | 131 ++- 4 files changed, 699 insertions(+), 22 deletions(-)
Re: [PATCH v2 0/2] hw/ufs: Add support MCQ
On 5/28/2024 11:31 AM, Minwoo Im wrote: UFSHCI 4.0 spec introduced MCQ(Multi-Circular Queue) to support multiple command queues for UFS controller. To test ufs-mcq path of kernel, MCQ emulated device would be a good choice to go with. The first patch added newly introduced fields in UFSHCI 4.0 to support MCQ. The other one made the actual changes for MCQ. v2: It fixed printing error event trace even in normal shutdown cases for SQ/CQ tear-down by checking whether each SQ/CQ is valid or not. The default value of mcq-maxq was updated to 2 from 1 to prevent the kernel from allocating a single queue as a poll_queue by default and to ensure that io_queues exist to handle device commands. Please review. Thanks, Minwoo Im (2): hw/ufs: Update MCQ-related fields to block/ufs.h hw/ufs: Add support MCQ of UFSHCI 4.0 hw/ufs/trace-events | 17 ++ hw/ufs/ufs.c| 478 ++-- hw/ufs/ufs.h| 98 - include/block/ufs.h | 131 +++- 4 files changed, 702 insertions(+), 22 deletions(-) Thank you for the patch. Reviewed-by: Jeuk Kim
Re: [PATCH 2/2] hw/ufs: Add support MCQ of UFSHCI 4.0
Thanks for your contribution! There are only two minor comments. Please check it and send patch v2. Thank you! On 5/21/2024 8:05 PM, Minwoo Im wrote: @@ -1288,12 +1717,21 @@ static void ufs_exit(PCIDevice *pci_dev) ufs_clear_req(>req_list[i]); } g_free(u->req_list); + +for (int i = 0; i < ARRAY_SIZE(u->sq); i++) { +ufs_mcq_delete_sq(u, i); Isn't it possible that trace_ufs_err_mcq_delete_cq_not_exists is printed even in a normal shutdown situation? If true, please fix it so that the ufs_err log is not printed in normal situation. +} +for (int i = 0; i < ARRAY_SIZE(u->cq); i++) { +ufs_mcq_delete_cq(u, i); +} } static Property ufs_props[] = { DEFINE_PROP_STRING("serial", UfsHc, params.serial), DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), +DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false), +DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 1), Please change this value to a value greater than or equal to 2.
[PULL 1/1] hw/ufs: Fix buffer overflow bug
From: Jeuk Kim It fixes the buffer overflow vulnerability in the ufs device. The bug was detected by sanitizers. You can reproduce it by: cat << EOF |\ qemu-system-x86_64 \ -display none -machine accel=qtest -m 512M -M q35 -nodefaults -drive \ file=null-co://,if=none,id=disk0 -device ufs,id=ufs_bus -device \ ufs-lu,drive=disk0,bus=ufs_bus -qtest stdio outl 0xcf8 0x8810 outl 0xcfc 0xe000 outl 0xcf8 0x8804 outw 0xcfc 0x06 write 0xe058 0x1 0xa7 write 0xa 0x1 0x50 EOF Resolves: #2299 Fixes: 329f16624499 ("hw/ufs: Support for Query Transfer Requests") Reported-by: Zheyu Ma Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 8 1 file changed, 8 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index eccdb852a0..bac78a32bb 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -126,6 +126,10 @@ static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + data_segment_length; +if (copy_size > sizeof(req->req_upiu)) { +copy_size = sizeof(req->req_upiu); +} + ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); if (ret) { trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); @@ -225,6 +229,10 @@ static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req) copy_size = rsp_upiu_byte_len; } +if (copy_size > sizeof(req->rsp_upiu)) { +copy_size = sizeof(req->rsp_upiu); +} + ret = ufs_addr_write(u, rsp_upiu_base_addr, >rsp_upiu, copy_size); if (ret) { trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr); -- 2.34.1
[PULL 0/1] ufs queue
From: Jeuk Kim The following changes since commit fd87be1dada5672f877e03c2ca8504458292c479: Merge tag 'accel-20240426' of https://github.com/philmd/qemu into staging (2024-04-26 15:28:13 -0700) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20240429 for you to fetch changes up to f2c8aeb1afefcda92054c448b21fc59cdd99db30: hw/ufs: Fix buffer overflow bug (2024-04-29 12:13:35 +0900) ufs queue - Fix ufs sanitizer vulnerability Jeuk Kim (1): hw/ufs: Fix buffer overflow bug hw/ufs/ufs.c | 8 1 file changed, 8 insertions(+)
[PATCH] hw/ufs: Fix buffer overflow bug
It fixes the buffer overflow vulnerability in the ufs device. The bug was detected by sanitizers. You can reproduce it by: cat << EOF |\ qemu-system-x86_64 \ -display none -machine accel=qtest -m 512M -M q35 -nodefaults -drive \ file=null-co://,if=none,id=disk0 -device ufs,id=ufs_bus -device \ ufs-lu,drive=disk0,bus=ufs_bus -qtest stdio outl 0xcf8 0x8810 outl 0xcfc 0xe000 outl 0xcf8 0x8804 outw 0xcfc 0x06 write 0xe058 0x1 0xa7 write 0xa 0x1 0x50 EOF Resolves: #2299 Fixes: 329f16624499 ("hw/ufs: Support for Query Transfer Requests") Reported-by: Zheyu Ma Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 8 1 file changed, 8 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index eccdb852a0..bac78a32bb 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -126,6 +126,10 @@ static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + data_segment_length; +if (copy_size > sizeof(req->req_upiu)) { +copy_size = sizeof(req->req_upiu); +} + ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); if (ret) { trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); @@ -225,6 +229,10 @@ static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req) copy_size = rsp_upiu_byte_len; } +if (copy_size > sizeof(req->rsp_upiu)) { +copy_size = sizeof(req->rsp_upiu); +} + ret = ufs_addr_write(u, rsp_upiu_base_addr, >rsp_upiu, copy_size); if (ret) { trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr); -- 2.34.1
[PATCH] hw/ufs: Raise interrupt on UIC power mode change
This patch allows the qemu ufs to raise an interrupt on the DME_SET (PA_PWRMODE) command. Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c| 15 ++- include/block/ufs.h | 65 + 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index eccdb852a0..e95f732a9f 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -301,6 +301,19 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val) * are implemented. */ switch (val) { +case UFS_UIC_CMD_DME_SET: +if (FIELD_EX32(u->reg.ucmdarg1, UCMDARG1, MIBattribute) == +UFS_UNIPRO_PA_PWRMODE) { +u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL); +u->reg.is = FIELD_DP32(u->reg.is, IS, UPMS, 1); +} +u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; +break; +case UFS_UIC_CMD_DME_GET: +case UFS_UIC_CMD_DME_PEER_GET: +case UFS_UIC_CMD_DME_PEER_SET: +u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; +break; case UFS_UIC_CMD_DME_LINK_STARTUP: u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, DP, 1); u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTRLRDY, 1); @@ -434,7 +447,6 @@ static const MemoryRegionOps ufs_mmio_ops = { }, }; - void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, uint8_t response, uint8_t scsi_status, uint16_t data_segment_length) @@ -1237,6 +1249,7 @@ static void ufs_init_hc(UfsHc *u) u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001); memset(>attributes, 0, sizeof(u->attributes)); +u->attributes.current_power_mode = 0x11; /* Active Power Mode */ u->attributes.max_data_in_size = 0x08; u->attributes.max_data_out_size = 0x08; u->attributes.ref_clk_freq = 0x01; /* 26 MHz */ diff --git a/include/block/ufs.h b/include/block/ufs.h index d61598b8f3..fe2f2ce944 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -125,6 +125,8 @@ REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr)) REG32(UICCMD, offsetof(UfsReg, uiccmd)) REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1)) +FIELD(UCMDARG1, GenSelectorIndex, 0, 16) +FIELD(UCMDARG1, MIBattribute, 16, 16) REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2)) REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3)) REG32(CCAP, offsetof(UfsReg, ccap)) @@ -1064,6 +1066,69 @@ typedef struct QEMU_PACKED UtpUpiuRsp { }; } UtpUpiuRsp; +/* + * PHY Adapter attributes + */ +#define UFS_UNIPRO_PA_PHY_TYPE 0x1500 +#define UFS_UNIPRO_PA_AVAILTXDATALANES 0x1520 +#define UFS_UNIPRO_PA_MAXTXSPEEDFAST 0x1521 +#define UFS_UNIPRO_PA_MAXTXSPEEDSLOW 0x1522 +#define UFS_UNIPRO_PA_MAXRXSPEEDFAST 0x1541 +#define UFS_UNIPRO_PA_MAXRXSPEEDSLOW 0x1542 +#define UFS_UNIPRO_PA_TXLINKSTARTUPHS 0x1544 +#define UFS_UNIPRO_PA_AVAILRXDATALANES 0x1540 +#define UFS_UNIPRO_PA_MINRXTRAILINGCLOCKS 0x1543 +#define UFS_UNIPRO_PA_LOCAL_TX_LCC_ENABLE 0x155E +#define UFS_UNIPRO_PA_ACTIVETXDATALANES 0x1560 +#define UFS_UNIPRO_PA_CONNECTEDTXDATALANES 0x1561 +#define UFS_UNIPRO_PA_TXFORCECLOCK 0x1562 +#define UFS_UNIPRO_PA_TXPWRMODE 0x1563 +#define UFS_UNIPRO_PA_TXTRAILINGCLOCKS 0x1564 +#define UFS_UNIPRO_PA_TXSPEEDFAST 0x1565 +#define UFS_UNIPRO_PA_TXSPEEDSLOW 0x1566 +#define UFS_UNIPRO_PA_TXPWRSTATUS 0x1567 +#define UFS_UNIPRO_PA_TXGEAR 0x1568 +#define UFS_UNIPRO_PA_TXTERMINATION 0x1569 +#define UFS_UNIPRO_PA_HSSERIES 0x156A +#define UFS_UNIPRO_PA_LEGACYDPHYESCDL 0x1570 +#define UFS_UNIPRO_PA_PWRMODE 0x1571 +#define UFS_UNIPRO_PA_ACTIVERXDATALANES 0x1580 +#define UFS_UNIPRO_PA_CONNECTEDRXDATALANES 0x1581 +#define UFS_UNIPRO_PA_RXPWRSTATUS 0x1582 +#define UFS_UNIPRO_PA_RXGEAR 0x1583 +#define UFS_UNIPRO_PA_RXTERMINATION 0x1584 +#define UFS_UNIPRO_PA_MAXRXPWMGEAR 0x1586 +#define UFS_UNIPRO_PA_MAXRXHSGEAR 0x1587 +#define UFS_UNIPRO_PA_PACPREQTIMEOUT 0x1590 +#define UFS_UNIPRO_PA_PACPREQEOBTIMEOUT 0x1591 +#define UFS_UNIPRO_PA_REMOTEVERINFO 0x15A0 +#define UFS_UNIPRO_PA_LOGICALLANEMAP 0x15A1 +#define UFS_UNIPRO_PA_SLEEPNOCONFIGTIME 0x15A2 +#define UFS_UNIPRO_PA_STALLNOCONFIGTIME 0x15A3 +#define UFS_UNIPRO_PA_SAVECONFIGTIME 0x15A4 +#define UFS_UNIPRO_PA_RXHSUNTERMCAP 0x15A5 +#define UFS_UNIPRO_PA_RXLSTERMCAP 0x15A6 +#define UFS_UNIPRO_PA_HIBERN8TIME 0x15A7 +#define UFS_UNIPRO_PA_LOCALVERINFO 0x15A9 +#define UFS_UNIPRO_PA_GRANULARITY 0x15AA +#define UFS_UNIPRO_PA_TACTIVATE 0x15A8 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA0 0x15B0 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA1 0x15B1 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA2 0x15B2 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA3 0x15B3 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA4 0x15B4 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA5 0x15B5 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA6 0x15B6 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA7 0x15B7 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA8 0x15B8 +#define UFS_UNIPRO_PA_PWRMODEUSERDATA9
Re: [PATCH 0/3] Support for Zoned UFS
I've already done all the ufs related review. If the SCSI maintainers approve this patchset, I'll put it in my tree and create a pull request. Thank you, Jeuk On 12/8/2023 3:09 PM, Daejun Park wrote: This patch enables zoned support for UFS devices. By applying this patch, a QEMU run can use parameters to configure whether each LU of each UFS is zoned, and the capacity, size, and max open zones. Zoned UFS is implemented by referencing ZBC2. (https://www.t10.org/members/w_zbc2.htm) Daejun Park (3): hw/ufs: Support for Zoned UFS hw/scsi: add mode sense support for zbc device tests/qtest: Add tests for Zoned UFS hw/scsi/scsi-disk.c| 13 +- hw/ufs/lu.c| 616 + hw/ufs/ufs.c | 6 +- hw/ufs/ufs.h | 32 +++ include/block/ufs.h| 31 +++ tests/qtest/ufs-test.c | 178 6 files changed, 870 insertions(+), 6 deletions(-)
[PULL 1/1] hw/ufs: avoid generating the same ID string for different LU devices
From: Akinobu Mita QEMU would not start when trying to create two UFS host controllers and a UFS logical unit for each with the following options: -device ufs,id=bus0 \ -device ufs-lu,drive=drive1,bus=bus0,lun=0 \ -device ufs,id=bus1 \ -device ufs-lu,drive=drive2,bus=bus1,lun=0 \ This is because the same ID string ("0:0:0/scsi-disk") is generated for both UFS logical units. To fix this issue, prepend the parent pci device's path to make the ID string unique. (":00:03.0/0:0:0/scsi-disk" and ":00:04.0/0:0:0/scsi-disk") Resolves: #2018 Fixes: 096434fea13a ("hw/ufs: Modify lu.c to share codes with SCSI subsystem") Signed-off-by: Akinobu Mita Reviewed-by: Jeuk Kim Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20231204150543.48252-1-akinobu.m...@gmail.com> Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 8 1 file changed, 8 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 68c5f1f6c9..eccdb852a0 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1323,9 +1323,17 @@ static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, return true; } +static char *ufs_bus_get_dev_path(DeviceState *dev) +{ +BusState *bus = qdev_get_parent_bus(dev); + +return qdev_get_dev_path(bus->parent); +} + static void ufs_bus_class_init(ObjectClass *class, void *data) { BusClass *bc = BUS_CLASS(class); +bc->get_dev_path = ufs_bus_get_dev_path; bc->check_address = ufs_bus_check_address; } -- 2.34.1
[PULL 0/1] ufs fix for 2023-12-05
From: Jeuk Kim The following changes since commit 1664d74c50739401c8b40e8b514d12b5fc250067: tests/avocado: Update yamon-bin-02.22.zip URL (2023-12-04 08:17:35 -0500) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20231205 for you to fetch changes up to 80a37b039ea9473d038bcef8bb64f4213affeae8: hw/ufs: avoid generating the same ID string for different LU devices (2023-12-05 13:57:18 +0900) ufs fixes for 8.2 - Fix QEMU not starting when creating two UFS host controllers Akinobu Mita (1): hw/ufs: avoid generating the same ID string for different LU devices hw/ufs/ufs.c | 8 1 file changed, 8 insertions(+)
Re: [PATCH-for-8.2?] hw/ufs: avoid generating the same ID string for different LU devices
Hi Philippe, Thank you for informing me. I want this issue is fixed for the 8.2 release. I created an issue like you said, but I couldn't assign the milestone (I'm guessing it's a permission problem). https://gitlab.com/qemu-project/qemu/-/issues/2018 Could you help me with this? Regardless, I'm going to submit a pull request to fix the issue right away. Thank you. On 4/12/23, Philippe Mathieu-Daudé wrote: > Hi Jeuk, > > On 4/12/23 17:50, Philippe Mathieu-Daudé wrote: >> On 4/12/23 16:05, Akinobu Mita wrote: >>> QEMU would not start when trying to create two UFS host controllers and >>> a UFS logical unit for each with the following options: >>> >>> -device ufs,id=bus0 \ >>> -device ufs-lu,drive=drive1,bus=bus0,lun=0 \ >>> -device ufs,id=bus1 \ >>> -device ufs-lu,drive=drive2,bus=bus1,lun=0 \ >>> >>> This is because the same ID string ("0:0:0/scsi-disk") is generated >>> for both UFS logical units. >>> >>> To fix this issue, prepend the parent pci device's path to make >>> the ID string unique. >>> (":00:03.0/0:0:0/scsi-disk" and ":00:04.0/0:0:0/scsi-disk") >>> >>> Fixes: 096434fea13a ("hw/ufs: Modify lu.c to share codes with SCSI >>> subsystem") > > If you think this must be fixed for the 8.2 release, please assign > a release blocker issues to the GitLab 8.2 milestone here: > https://gitlab.com/qemu-project/qemu/-/milestones/10 > >>> Signed-off-by: Akinobu Mita >> >> Reviewed-by: Philippe Mathieu-Daudé >> >>> --- >>> hw/ufs/ufs.c 8 >>> 1 file changed, 8 insertions(+) >>> >>> diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c >>> index 68c5f1f6c9..eccdb852a0 100644 >>> --- a/hw/ufs/ufs.c >>> +++ b/hw/ufs/ufs.c >>> @@ -1323,9 +1323,17 @@ static bool ufs_bus_check_address(BusState >>> *qbus, DeviceState *qdev, >>> return true; >>> } >>> +static char *ufs_bus_get_dev_path(DeviceState *dev) >>> +{ >>> + BusState *bus = qdev_get_parent_bus(dev); >>> + >>> + return qdev_get_dev_path(bus->parent); >>> +} >>> + >>> static void ufs_bus_class_init(ObjectClass *class, void *data) >>> { >>> BusClass *bc = BUS_CLASS(class); >>> + bc->get_dev_path = ufs_bus_get_dev_path; >>> bc->check_address = ufs_bus_check_address; >>> }
Re: [PATCH-for-8.2?] hw/ufs: avoid generating the same ID string for different LU devices
On 4/12/23 16:05, Akinobu Mita wrote: > QEMU would not start when trying to create two UFS host controllers and > a UFS logical unit for each with the following options: > > -device ufs,id=bus0 \ > -device ufs-lu,drive=drive1,bus=bus0,lun=0 \ > -device ufs,id=bus1 \ > -device ufs-lu,drive=drive2,bus=bus1,lun=0 \ > > This is because the same ID string ("0:0:0/scsi-disk") is generated > for both UFS logical units. > > To fix this issue, prepend the parent pci device's path to make > the ID string unique. > (":00:03.0/0:0:0/scsi-disk" and ":00:04.0/0:0:0/scsi-disk") > > Fixes: 096434fea13a ("hw/ufs: Modify lu.c to share codes with SCSI subsystem") > Signed-off-by: Akinobu Mita Reviewed-by: Jeuk Kim Thank you!
Re: [PATCH] hw/ufs: Modify lu.c to share codes with SCSI subsystem
On 10/30/2023 1:11 PM, Philippe Mathieu-Daudé wrote: Hi Jeuk, On 20/10/23 03:51, Jeuk Kim wrote: This patch removes the code that ufs-lu was duplicating from scsi-hd and allows them to share code. It makes ufs-lu have a virtual scsi-bus and scsi-hd internally. This allows scsi related commands to be passed thorugh to the scsi-hd. The query request and nop command work the same as the existing logic. Well-known lus do not have a virtual scsi-bus and scsi-hd, and handle the necessary scsi commands by emulating them directly. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1473 +++- I liked this patch intent, but almost 1500 lines changed in a single patch make it impossible to review. Ideally each patch shouldn't modify more than 100 lines, otherwise reviewers are either exhausted or can't be careful enough and miss possible bugs or design issues. Regards, Phil. Hi Phil, Thanks for the comment. I thought that since most of the code fixes were code removals and were functionally bundled together, it was right to submit them as a single patch. However, I agree that it was inconsiderate of the reviewers, and I apologize for that. Next time, I'll size the patch appropriately as you suggest. Thanks, Jeuk
[PULL 1/1] hw/ufs: Modify lu.c to share codes with SCSI subsystem
From: Jeuk Kim This patch removes the code that ufs-lu was duplicating from scsi-hd and allows them to share code. It makes ufs-lu have a virtual scsi-bus and scsi-hd internally. This allows scsi related commands to be passed thorugh to the scsi-hd. The query request and nop command work the same as the existing logic. Well-known lus do not have a virtual scsi-bus and scsi-hd, and handle the necessary scsi commands by emulating them directly. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c| 1473 +++- hw/ufs/trace-events| 25 - hw/ufs/ufs.c | 202 +- hw/ufs/ufs.h | 36 +- include/block/ufs.h|2 +- tests/qtest/ufs-test.c | 37 +- 6 files changed, 315 insertions(+), 1460 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 13b5e37b53..81bfff9b4e 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -19,57 +19,117 @@ #include "trace.h" #include "ufs.h" -/* - * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, - * with minor adjustments to make it work for UFS. - */ +#define SCSI_COMMAND_FAIL (-1) -#define SCSI_DMA_BUF_SIZE (128 * KiB) -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_INQUIRY_DATA_SIZE 36 -#define SCSI_MAX_MODE_LEN 256 - -typedef struct UfsSCSIReq { -SCSIRequest req; -/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ -uint64_t sector; -uint32_t sector_count; -uint32_t buflen; -bool started; -bool need_fua_emulation; -struct iovec iov; -QEMUIOVector qiov; -BlockAcctCookie acct; -} UfsSCSIReq; - -static void ufs_scsi_free_request(SCSIRequest *req) +static void ufs_build_upiu_sense_data(UfsRequest *req, uint8_t *sense, + uint32_t sense_len) { -UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); +req->rsp_upiu.sr.sense_data_len = cpu_to_be16(sense_len); +assert(sense_len <= SCSI_SENSE_LEN); +memcpy(req->rsp_upiu.sr.sense_data, sense, sense_len); +} + +static void ufs_build_scsi_response_upiu(UfsRequest *req, uint8_t *sense, + uint32_t sense_len, + uint32_t transfered_len, + int16_t status) +{ +uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len); +uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCCESS; +uint16_t data_segment_length; + +if (expected_len > transfered_len) { +req->rsp_upiu.sr.residual_transfer_count = +cpu_to_be32(expected_len - transfered_len); +flags |= UFS_UPIU_FLAG_UNDERFLOW; +} else if (expected_len < transfered_len) { +req->rsp_upiu.sr.residual_transfer_count = +cpu_to_be32(transfered_len - expected_len); +flags |= UFS_UPIU_FLAG_OVERFLOW; +} -qemu_vfree(r->iov.iov_base); +if (status != 0) { +ufs_build_upiu_sense_data(req, sense, sense_len); +response = UFS_COMMAND_RESULT_FAIL; +} + +data_segment_length = +cpu_to_be16(sense_len + sizeof(req->rsp_upiu.sr.sense_data_len)); +ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, response, + status, data_segment_length); } -static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) { -trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, - sense.ascq); -scsi_req_build_sense(>req, sense); -scsi_req_complete(>req, CHECK_CONDITION); +UfsRequest *req = scsi_req->hba_private; +int16_t status = scsi_req->status; + +uint32_t transfered_len = scsi_req->cmd.xfer - resid; + +ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len, + transfered_len, status); + +ufs_complete_req(req, UFS_REQUEST_SUCCESS); + +scsi_req->hba_private = NULL; +scsi_req_unref(scsi_req); } -static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, - uint32_t outbuf_len) +static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) { -UfsHc *u = UFS(req->bus->qbus.parent); -UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); -uint8_t page_code = req->cmd.buf[2]; -int start, buflen = 0; +UfsRequest *req = scsi_req->hba_private; +return req->sg; +} + +static const struct SCSIBusInfo ufs_scsi_info = { +.tcq = true, +.max_target = 0, +.max_lun = UFS_MAX_LUS, +.max_channel = 0, + +.get_sg_list = ufs_get_sg_list, +.complete = ufs_scsi_command_complete, +}; + +static int ufs_emulate_report_luns(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = req->hc; +int len = 0; -if
[PULL 0/1] ufs queue
From: Jeuk Kim The following changes since commit c60be6e3e38cb36dc66129e757ec4b34152232be: Merge tag 'pull-sp-20231025' of https://gitlab.com/rth7680/qemu into staging (2023-10-27 09:43:53 +0900) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20231030 for you to fetch changes up to 096434fea13acd19f4ead00cdf9babea8dc7e61e: hw/ufs: Modify lu.c to share codes with SCSI subsystem (2023-10-30 10:28:04 +0900) ufs queue: * Modify lu.c to share codes with SCSI Jeuk Kim (1): hw/ufs: Modify lu.c to share codes with SCSI subsystem hw/ufs/lu.c| 1473 hw/ufs/trace-events| 25 - hw/ufs/ufs.c | 202 +-- hw/ufs/ufs.h | 36 +- include/block/ufs.h|2 +- tests/qtest/ufs-test.c | 37 +- 6 files changed, 315 insertions(+), 1460 deletions(-)
[PATCH] hw/ufs: Modify lu.c to share codes with SCSI subsystem
This patch removes the code that ufs-lu was duplicating from scsi-hd and allows them to share code. It makes ufs-lu have a virtual scsi-bus and scsi-hd internally. This allows scsi related commands to be passed thorugh to the scsi-hd. The query request and nop command work the same as the existing logic. Well-known lus do not have a virtual scsi-bus and scsi-hd, and handle the necessary scsi commands by emulating them directly. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c| 1473 +++- hw/ufs/trace-events| 25 - hw/ufs/ufs.c | 202 +- hw/ufs/ufs.h | 36 +- include/block/ufs.h|2 +- tests/qtest/ufs-test.c | 37 +- 6 files changed, 315 insertions(+), 1460 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 13b5e37b53..81bfff9b4e 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -19,57 +19,117 @@ #include "trace.h" #include "ufs.h" -/* - * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, - * with minor adjustments to make it work for UFS. - */ +#define SCSI_COMMAND_FAIL (-1) -#define SCSI_DMA_BUF_SIZE (128 * KiB) -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_INQUIRY_DATA_SIZE 36 -#define SCSI_MAX_MODE_LEN 256 - -typedef struct UfsSCSIReq { -SCSIRequest req; -/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ -uint64_t sector; -uint32_t sector_count; -uint32_t buflen; -bool started; -bool need_fua_emulation; -struct iovec iov; -QEMUIOVector qiov; -BlockAcctCookie acct; -} UfsSCSIReq; - -static void ufs_scsi_free_request(SCSIRequest *req) +static void ufs_build_upiu_sense_data(UfsRequest *req, uint8_t *sense, + uint32_t sense_len) { -UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); +req->rsp_upiu.sr.sense_data_len = cpu_to_be16(sense_len); +assert(sense_len <= SCSI_SENSE_LEN); +memcpy(req->rsp_upiu.sr.sense_data, sense, sense_len); +} + +static void ufs_build_scsi_response_upiu(UfsRequest *req, uint8_t *sense, + uint32_t sense_len, + uint32_t transfered_len, + int16_t status) +{ +uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len); +uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCCESS; +uint16_t data_segment_length; + +if (expected_len > transfered_len) { +req->rsp_upiu.sr.residual_transfer_count = +cpu_to_be32(expected_len - transfered_len); +flags |= UFS_UPIU_FLAG_UNDERFLOW; +} else if (expected_len < transfered_len) { +req->rsp_upiu.sr.residual_transfer_count = +cpu_to_be32(transfered_len - expected_len); +flags |= UFS_UPIU_FLAG_OVERFLOW; +} -qemu_vfree(r->iov.iov_base); +if (status != 0) { +ufs_build_upiu_sense_data(req, sense, sense_len); +response = UFS_COMMAND_RESULT_FAIL; +} + +data_segment_length = +cpu_to_be16(sense_len + sizeof(req->rsp_upiu.sr.sense_data_len)); +ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, response, + status, data_segment_length); } -static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) { -trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, - sense.ascq); -scsi_req_build_sense(>req, sense); -scsi_req_complete(>req, CHECK_CONDITION); +UfsRequest *req = scsi_req->hba_private; +int16_t status = scsi_req->status; + +uint32_t transfered_len = scsi_req->cmd.xfer - resid; + +ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len, + transfered_len, status); + +ufs_complete_req(req, UFS_REQUEST_SUCCESS); + +scsi_req->hba_private = NULL; +scsi_req_unref(scsi_req); } -static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, - uint32_t outbuf_len) +static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) { -UfsHc *u = UFS(req->bus->qbus.parent); -UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); -uint8_t page_code = req->cmd.buf[2]; -int start, buflen = 0; +UfsRequest *req = scsi_req->hba_private; +return req->sg; +} + +static const struct SCSIBusInfo ufs_scsi_info = { +.tcq = true, +.max_target = 0, +.max_lun = UFS_MAX_LUS, +.max_channel = 0, + +.get_sg_list = ufs_get_sg_list, +.complete = ufs_scsi_command_complete, +}; + +static int ufs_emulate_report_luns(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = req->hc; +int len = 0; -if (outbuf_len < S
[PULL v2 2/2] hw/ufs: Fix incorrect register fields
From: Jeuk Kim This patch fixes invalid ufs register fields. This fixes an issue reported by Bin Meng that caused ufs to fail over riscv. Fixes: bc4e68d362ec ("hw/ufs: Initial commit for emulated Universal-Flash-Storage") Signed-off-by: Jeuk Kim Reported-by: Bin Meng Reviewed-by: Bin Meng Tested-by: Bin Meng --- include/block/ufs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/block/ufs.h b/include/block/ufs.h index fd884eb8ce..7631a5af10 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -111,14 +111,14 @@ REG32(UECT, offsetof(UfsReg, uect)) REG32(UECDME, offsetof(UfsReg, uecdme)) REG32(UTRIACR, offsetof(UfsReg, utriacr)) REG32(UTRLBA, offsetof(UfsReg, utrlba)) -FIELD(UTRLBA, UTRLBA, 9, 22) +FIELD(UTRLBA, UTRLBA, 10, 22) REG32(UTRLBAU, offsetof(UfsReg, utrlbau)) REG32(UTRLDBR, offsetof(UfsReg, utrldbr)) REG32(UTRLCLR, offsetof(UfsReg, utrlclr)) REG32(UTRLRSR, offsetof(UfsReg, utrlrsr)) REG32(UTRLCNR, offsetof(UfsReg, utrlcnr)) REG32(UTMRLBA, offsetof(UfsReg, utmrlba)) -FIELD(UTMRLBA, UTMRLBA, 9, 22) +FIELD(UTMRLBA, UTMRLBA, 10, 22) REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau)) REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr)) REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) -- 2.34.1
[PULL v2 0/2] hw/ufs: fixes
From: Jeuk Kim The following changes since commit 63011373ad22c794a013da69663c03f1297a5c56: Merge tag 'pull-riscv-to-apply-20231012-1' of https://github.com/alistair23/qemu into staging (2023-10-12 10:24:44 -0400) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20231013 for you to fetch changes up to ebca80bbdb5c1650e4b753a3d13b43634e7dfe05: hw/ufs: Fix incorrect register fields (2023-10-13 13:56:28 +0900) hw/ufs: fixes Jeuk Kim (2): hw/ufs: Fix code coverity issues hw/ufs: Fix incorrect register fields hw/ufs/lu.c| 16 +++- hw/ufs/ufs.c | 10 +- include/block/ufs.h| 4 ++-- tests/qtest/ufs-test.c | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-)
[PULL v2 1/2] hw/ufs: Fix code coverity issues
From: Jeuk Kim Fixed four ufs-related coverity issues. The coverity issues and fixes are as follows 1. CID 1519042: Security issue with the rand() function Changed to use a fixed value (0xab) instead of rand() as the value for testing 2. CID 1519043: Dereference after null check Removed useless (redundant) null checks 3. CID 1519050: Out-of-bounds access issue Fix to pass an array type variable to find_first_bit and find_next_bit using DECLARE_BITMAP() 4. CID 1519051: Out-of-bounds read issue Fix incorrect range check for lun Fix coverity CID: 1519042 1519043 1519050 1519051 Signed-off-by: Jeuk Kim --- hw/ufs/lu.c| 16 +++- hw/ufs/ufs.c | 10 +- tests/qtest/ufs-test.c | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index e1c46bddb1..13b5e37b53 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -1345,13 +1345,12 @@ static void ufs_lu_realize(SCSIDevice *dev, Error **errp) return; } -if (lu->qdev.conf.blk) { -ctx = blk_get_aio_context(lu->qdev.conf.blk); -aio_context_acquire(ctx); -if (!blkconf_blocksizes(>qdev.conf, errp)) { -goto out; -} +ctx = blk_get_aio_context(lu->qdev.conf.blk); +aio_context_acquire(ctx); +if (!blkconf_blocksizes(>qdev.conf, errp)) { +goto out; } + lu->qdev.blocksize = UFS_BLOCK_SIZE; blk_get_geometry(lu->qdev.conf.blk, _sectors); nb_blocks = nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE); @@ -1367,10 +1366,9 @@ static void ufs_lu_realize(SCSIDevice *dev, Error **errp) } ufs_lu_brdv_init(lu, errp); + out: -if (ctx) { -aio_context_release(ctx); -} +aio_context_release(ctx); } static void ufs_lu_unrealize(SCSIDevice *dev) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 0ecedb9aed..2e6d582cc3 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -258,7 +258,7 @@ static void ufs_irq_check(UfsHc *u) static void ufs_process_db(UfsHc *u, uint32_t val) { -unsigned long doorbell; +DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS); uint32_t slot; uint32_t nutrs = u->params.nutrs; UfsRequest *req; @@ -268,8 +268,8 @@ static void ufs_process_db(UfsHc *u, uint32_t val) return; } -doorbell = val; -slot = find_first_bit(, nutrs); +doorbell[0] = val; +slot = find_first_bit(doorbell, nutrs); while (slot < nutrs) { req = >req_list[slot]; @@ -285,7 +285,7 @@ static void ufs_process_db(UfsHc *u, uint32_t val) trace_ufs_process_db(slot); req->state = UFS_REQUEST_READY; -slot = find_next_bit(, nutrs, slot + 1); +slot = find_next_bit(doorbell, nutrs, slot + 1); } qemu_bh_schedule(u->doorbell_bh); @@ -838,7 +838,7 @@ static QueryRespCode ufs_read_unit_desc(UfsRequest *req) uint8_t lun = req->req_upiu.qr.index; if (lun != UFS_UPIU_RPMB_WLUN && -(lun > UFS_MAX_LUS || u->lus[lun] == NULL)) { +(lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) { trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun); return UFS_QUERY_RESULT_INVALID_INDEX; } diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index ed3dbca154..15d467630c 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -497,7 +497,7 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpuint(block_size, ==, 4096); /* Write data */ -memset(write_buf, rand() % 255 + 1, block_size); +memset(write_buf, 0xab, block_size); ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, , _upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); -- 2.34.1
[PULL 1/2] hw/ufs: Fix code coverity issues
From: Jeuk Kim Fixed four ufs-related coverity issues. The coverity issues and fixes are as follows 1. CID 1519042: Security issue with the rand() function Changed to use a fixed value (0xab) instead of rand() as the value for testing 2. CID 1519043: Dereference after null check Removed useless (redundant) null checks 3. CID 1519050: Out-of-bounds access issue Fix to pass an array type variable to find_first_bit and find_next_bit using DECLARE_BITMAP() 4. CID 1519051: Out-of-bounds read issue Fix incorrect range check for lun Fix coverity CID: 1519042 1519043 1519050 1519051 Signed-off-by: Jeuk Kim --- hw/ufs/lu.c| 16 +++- hw/ufs/ufs.c | 10 +- tests/qtest/ufs-test.c | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index e1c46bddb1..13b5e37b53 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -1345,13 +1345,12 @@ static void ufs_lu_realize(SCSIDevice *dev, Error **errp) return; } -if (lu->qdev.conf.blk) { -ctx = blk_get_aio_context(lu->qdev.conf.blk); -aio_context_acquire(ctx); -if (!blkconf_blocksizes(>qdev.conf, errp)) { -goto out; -} +ctx = blk_get_aio_context(lu->qdev.conf.blk); +aio_context_acquire(ctx); +if (!blkconf_blocksizes(>qdev.conf, errp)) { +goto out; } + lu->qdev.blocksize = UFS_BLOCK_SIZE; blk_get_geometry(lu->qdev.conf.blk, _sectors); nb_blocks = nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE); @@ -1367,10 +1366,9 @@ static void ufs_lu_realize(SCSIDevice *dev, Error **errp) } ufs_lu_brdv_init(lu, errp); + out: -if (ctx) { -aio_context_release(ctx); -} +aio_context_release(ctx); } static void ufs_lu_unrealize(SCSIDevice *dev) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 0ecedb9aed..b73eb3deaf 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -258,7 +258,7 @@ static void ufs_irq_check(UfsHc *u) static void ufs_process_db(UfsHc *u, uint32_t val) { -unsigned long doorbell; +DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS); uint32_t slot; uint32_t nutrs = u->params.nutrs; UfsRequest *req; @@ -268,8 +268,8 @@ static void ufs_process_db(UfsHc *u, uint32_t val) return; } -doorbell = val; -slot = find_first_bit(, nutrs); +memcpy(doorbell, , sizeof(val)); +slot = find_first_bit(doorbell, nutrs); while (slot < nutrs) { req = >req_list[slot]; @@ -285,7 +285,7 @@ static void ufs_process_db(UfsHc *u, uint32_t val) trace_ufs_process_db(slot); req->state = UFS_REQUEST_READY; -slot = find_next_bit(, nutrs, slot + 1); +slot = find_next_bit(doorbell, nutrs, slot + 1); } qemu_bh_schedule(u->doorbell_bh); @@ -838,7 +838,7 @@ static QueryRespCode ufs_read_unit_desc(UfsRequest *req) uint8_t lun = req->req_upiu.qr.index; if (lun != UFS_UPIU_RPMB_WLUN && -(lun > UFS_MAX_LUS || u->lus[lun] == NULL)) { +(lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) { trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun); return UFS_QUERY_RESULT_INVALID_INDEX; } diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index ed3dbca154..15d467630c 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -497,7 +497,7 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpuint(block_size, ==, 4096); /* Write data */ -memset(write_buf, rand() % 255 + 1, block_size); +memset(write_buf, 0xab, block_size); ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, , _upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); -- 2.34.1
[PULL 0/2] hw/ufs: fixes
From: Jeuk Kim The following changes since commit a51e5124a655b3dad80b36b18547cb1eca2c5eb2: Merge tag 'pull-omnibus-111023-1' of https://gitlab.com/stsquad/qemu into staging (2023-10-11 09:43:10 -0400) are available in the Git repository at: https://gitlab.com/jeuk20.kim/qemu.git tags/pull-ufs-20231012 for you to fetch changes up to 8466aa53d623377e114895c6563face25370d7da: hw/ufs: Fix incorrect register fields (2023-10-12 14:29:20 +0900) hw/ufs: fixes Jeuk Kim (2): hw/ufs: Fix code coverity issues hw/ufs: Fix incorrect register fields hw/ufs/lu.c| 16 +++- hw/ufs/ufs.c | 10 +- include/block/ufs.h| 4 ++-- tests/qtest/ufs-test.c | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-)
[PULL 2/2] hw/ufs: Fix incorrect register fields
From: Jeuk Kim This patch fixes invalid ufs register fields. This fixes an issue reported by Bin Meng that caused ufs to fail over riscv. Fixes: bc4e68d362ec ("hw/ufs: Initial commit for emulated Universal-Flash-Storage") Signed-off-by: Jeuk Kim Reported-by: Bin Meng Reviewed-by: Bin Meng Tested-by: Bin Meng --- include/block/ufs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/block/ufs.h b/include/block/ufs.h index fd884eb8ce..7631a5af10 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -111,14 +111,14 @@ REG32(UECT, offsetof(UfsReg, uect)) REG32(UECDME, offsetof(UfsReg, uecdme)) REG32(UTRIACR, offsetof(UfsReg, utriacr)) REG32(UTRLBA, offsetof(UfsReg, utrlba)) -FIELD(UTRLBA, UTRLBA, 9, 22) +FIELD(UTRLBA, UTRLBA, 10, 22) REG32(UTRLBAU, offsetof(UfsReg, utrlbau)) REG32(UTRLDBR, offsetof(UfsReg, utrldbr)) REG32(UTRLCLR, offsetof(UfsReg, utrlclr)) REG32(UTRLRSR, offsetof(UfsReg, utrlrsr)) REG32(UTRLCNR, offsetof(UfsReg, utrlcnr)) REG32(UTMRLBA, offsetof(UfsReg, utmrlba)) -FIELD(UTMRLBA, UTMRLBA, 9, 22) +FIELD(UTMRLBA, UTMRLBA, 10, 22) REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau)) REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr)) REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) -- 2.34.1
[PATCH] hw/ufs: Fix incorrect register fields
From: Jeuk Kim This patch fixes invalid ufs register fields. This fixes an issue reported by Bin Meng that caused ufs to fail over riscv. Signed-off-by: Jeuk Kim --- include/block/ufs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/block/ufs.h b/include/block/ufs.h index fd884eb8ce..7631a5af10 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -111,14 +111,14 @@ REG32(UECT, offsetof(UfsReg, uect)) REG32(UECDME, offsetof(UfsReg, uecdme)) REG32(UTRIACR, offsetof(UfsReg, utriacr)) REG32(UTRLBA, offsetof(UfsReg, utrlba)) -FIELD(UTRLBA, UTRLBA, 9, 22) +FIELD(UTRLBA, UTRLBA, 10, 22) REG32(UTRLBAU, offsetof(UfsReg, utrlbau)) REG32(UTRLDBR, offsetof(UfsReg, utrldbr)) REG32(UTRLCLR, offsetof(UfsReg, utrlclr)) REG32(UTRLRSR, offsetof(UfsReg, utrlrsr)) REG32(UTRLCNR, offsetof(UfsReg, utrlcnr)) REG32(UTMRLBA, offsetof(UfsReg, utmrlba)) -FIELD(UTMRLBA, UTMRLBA, 9, 22) +FIELD(UTMRLBA, UTMRLBA, 10, 22) REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau)) REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr)) REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) -- 2.34.1
Ping: Re: [PULL 4/5] hw/ufs: Support for UFS logical unit
Dear Paolo This is a ping for the following. If you don't mind, could you give me some feedback? Thank you very much. Jeuk On 23. 9. 21. 17:38, Jeuk Kim wrote: Dear Paolo Hi. I've been looking into how ufs-lu can share code with scsi-hd. I have verified that ufs-lu can use scsi-hd's code, and I would like to modify it to do so. I've validated two possible fixes. I'd like to hear your thoughts. Option1. As you mentioned, using ufsbus, which inherits from scsibus, removes the ufs-lu device type and use scsi-hd. (like -device ufs,id=ufs0 -device scsi-hd,bus=ufs0) I've verified that this method is implementable, except for one problem. Because we are using the scsi-hd type instead of the ufs-lu type, the ufs has to manage all the ufs-lu specific data (such as the unit descriptor). However, since there is no ufs_lu_realize() function, we need a way to notify the ufs when a new scsi-hd device is added. Would there be a way to let the ufs know that a new scsi-hd has been added at scsi_hd_realize() time? Option 2. Use qdev_new() & qdev_realize() to make ufs-lu have a virtual scsi bus and scsi-hd. The ufs-lu can pass through SCSI commands to the virtual scsi-hd. This is similar to the method used by the device "usb-storage". With this method, I can keep the ufs-lu device type (ufs_lu_realize() makes it convenient to manage ufs-lu related data) and avoid duplicating code with scsi-hd. So I prefer this approach, but the annotation for "usb-storage" is marked with a "Hack alert", so I'm not sure if this is the right way. The code can be found in usb_msd_storage_realize() (hw/usb/dev-storage-classic.c:51). I am wondering if you could give me some advice on this and your preferred direction for fixing it. Thank you so much. Jeuk
Re: [PULL 4/5] hw/ufs: Support for UFS logical unit
On 2023-09-15 4:59 PM, Paolo Bonzini wrote: On 9/15/23 00:19, Jeuk Kim wrote: First, ufs-lu has a feature called "unit descriptor". This feature shows the status of the ufs-lu and only works with UFS-specific "query request" commands, not SCSI commands. This looks like something that can be implemented in the UFS subsystem. UFS also has something called a well-known lu. Unlike typical SCSI devices, where each lu is independent, UFS can control other lu's through the well-known lu. This can also be implemented in UfsBus. Finally, UFS-LU will have features that SCSI-HD does not have, such as the zone block command. These should be implemented in scsi-hd as well. In addition to this, I wanted some scsi commands to behave differently from scsi-hd, for example, the Inquiry command should read "QEMU UFS" instead of "QEMU HARDDISK", and the mode_sense_page command should have a different result. Some of these don't have much justification, and others (such as the control page) could be done in scsi-hd as well. We should look into cleaning this up and making ufs-lu share a lot more code with scsi-hd; possibly even supporting -device scsi-hd with UFS devices. I am not going to ask you for a revert, but if this is not done before 8.2 is out, I will ask you to disable it by default in hw/ufs/Kconfig. In the future, please Cc the SCSI maintainers for UFS patches. Paolo Dear Paolo Hi. I've been looking into how ufs-lu can share code with scsi-hd. I have verified that ufs-lu can use scsi-hd's code, and I would like to modify it to do so. I've validated two possible fixes. I'd like to hear your thoughts. Option1. As you mentioned, using ufsbus, which inherits from scsibus, removes the ufs-lu device type and use scsi-hd. (like -device ufs,id=ufs0 -device scsi-hd,bus=ufs0) I've verified that this method is implementable, except for one problem. Because we are using the scsi-hd type instead of the ufs-lu type, the ufs has to manage all the ufs-lu specific data (such as the unit descriptor). However, since there is no ufs_lu_realize() function, we need a way to notify the ufs when a new scsi-hd device is added. Would there be a way to let the ufs know that a new scsi-hd has been added at scsi_hd_realize() time? Option 2. Use qdev_new() & qdev_realize() to make ufs-lu have a virtual scsi bus and scsi-hd. The ufs-lu can pass through SCSI commands to the virtual scsi-hd. This is similar to the method used by the device "usb-storage". With this method, I can keep the ufs-lu device type (ufs_lu_realize() makes it convenient to manage ufs-lu related data) and avoid duplicating code with scsi-hd. So I prefer this approach, but the annotation for "usb-storage" is marked with a "Hack alert", so I'm not sure if this is the right way. The code can be found in usb_msd_storage_realize() (hw/usb/dev-storage-classic.c:51). I am wondering if you could give me some advice on this and your preferred direction for fixing it. Thank you so much. Jeuk
Re: [PULL 4/5] hw/ufs: Support for UFS logical unit
On 2023-09-18 오후 1:41, Jeuk Kim wrote: On 2023-09-15 16:59, Paolo Bonzini wrote: On 9/15/23 00:19, Jeuk Kim wrote: First, ufs-lu has a feature called "unit descriptor". This feature shows the status of the ufs-lu and only works with UFS-specific "query request" commands, not SCSI commands. This looks like something that can be implemented in the UFS subsystem. UFS also has something called a well-known lu. Unlike typical SCSI devices, where each lu is independent, UFS can control other lu's through the well-known lu. This can also be implemented in UfsBus. Finally, UFS-LU will have features that SCSI-HD does not have, such as the zone block command. These should be implemented in scsi-hd as well. In addition to this, I wanted some scsi commands to behave differently from scsi-hd, for example, the Inquiry command should read "QEMU UFS" instead of "QEMU HARDDISK", and the mode_sense_page command should have a different result. Some of these don't have much justification, and others (such as the control page) could be done in scsi-hd as well. We should look into cleaning this up and making ufs-lu share a lot more code with scsi-hd; possibly even supporting -device scsi-hd with UFS devices. I am not going to ask you for a revert, but if this is not done before 8.2 is out, I will ask you to disable it by default in hw/ufs/Kconfig. In the future, please Cc the SCSI maintainers for UFS patches. Paolo Thanks for the comment. ufs-lu took most of its code from scsi-hd, so I completely agree that we should make scsi-hd code shareable to reduce code redundancy and make it better. Sorry about it. This sentence is misleading. I meant to say "I completely agree that ufs-lu should be made to reuse scsi-hd code to reduce code redundancy and improve code quality." I will fix it and get back to you soon. Thank you. Sincerely, Jeuk Thanks
Re: [PULL 4/5] hw/ufs: Support for UFS logical unit
On 2023-09-15 16:59, Paolo Bonzini wrote: On 9/15/23 00:19, Jeuk Kim wrote: First, ufs-lu has a feature called "unit descriptor". This feature shows the status of the ufs-lu and only works with UFS-specific "query request" commands, not SCSI commands. This looks like something that can be implemented in the UFS subsystem. UFS also has something called a well-known lu. Unlike typical SCSI devices, where each lu is independent, UFS can control other lu's through the well-known lu. This can also be implemented in UfsBus. Finally, UFS-LU will have features that SCSI-HD does not have, such as the zone block command. These should be implemented in scsi-hd as well. In addition to this, I wanted some scsi commands to behave differently from scsi-hd, for example, the Inquiry command should read "QEMU UFS" instead of "QEMU HARDDISK", and the mode_sense_page command should have a different result. Some of these don't have much justification, and others (such as the control page) could be done in scsi-hd as well. We should look into cleaning this up and making ufs-lu share a lot more code with scsi-hd; possibly even supporting -device scsi-hd with UFS devices. I am not going to ask you for a revert, but if this is not done before 8.2 is out, I will ask you to disable it by default in hw/ufs/Kconfig. In the future, please Cc the SCSI maintainers for UFS patches. Paolo Thanks for the comment. ufs-lu took most of its code from scsi-hd, so I completely agree that we should make scsi-hd code shareable to reduce code redundancy and make it better. I will fix it and get back to you soon. Thank you. Sincerely, Jeuk
[PATCH] hw/ufs: Fix code coverity issues
From: Jeuk Kim Fixed four ufs-related coverity issues. The coverity issues and fixes are as follows 1. CID 1519042: Security issue with the rand() function Changed to use a fixed value (0xab) instead of rand() as the value for testing 2. CID 1519043: Dereference after null check Removed useless (redundant) null checks 3. CID 1519050: Out-of-bounds access issue Fix to pass an array type variable to find_first_bit and find_next_bit using DECLARE_BITMAP() 4. CID 1519051: Out-of-bounds read issue Fix incorrect range check for lun Fix coverity CID: 1519042 1519043 1519050 1519051 Signed-off-by: Jeuk Kim --- hw/ufs/lu.c| 16 +++- hw/ufs/ufs.c | 10 +- tests/qtest/ufs-test.c | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index e1c46bddb1..13b5e37b53 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -1345,13 +1345,12 @@ static void ufs_lu_realize(SCSIDevice *dev, Error **errp) return; } -if (lu->qdev.conf.blk) { -ctx = blk_get_aio_context(lu->qdev.conf.blk); -aio_context_acquire(ctx); -if (!blkconf_blocksizes(>qdev.conf, errp)) { -goto out; -} +ctx = blk_get_aio_context(lu->qdev.conf.blk); +aio_context_acquire(ctx); +if (!blkconf_blocksizes(>qdev.conf, errp)) { +goto out; } + lu->qdev.blocksize = UFS_BLOCK_SIZE; blk_get_geometry(lu->qdev.conf.blk, _sectors); nb_blocks = nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE); @@ -1367,10 +1366,9 @@ static void ufs_lu_realize(SCSIDevice *dev, Error **errp) } ufs_lu_brdv_init(lu, errp); + out: -if (ctx) { -aio_context_release(ctx); -} +aio_context_release(ctx); } static void ufs_lu_unrealize(SCSIDevice *dev) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 0ecedb9aed..b73eb3deaf 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -258,7 +258,7 @@ static void ufs_irq_check(UfsHc *u) static void ufs_process_db(UfsHc *u, uint32_t val) { -unsigned long doorbell; +DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS); uint32_t slot; uint32_t nutrs = u->params.nutrs; UfsRequest *req; @@ -268,8 +268,8 @@ static void ufs_process_db(UfsHc *u, uint32_t val) return; } -doorbell = val; -slot = find_first_bit(, nutrs); +memcpy(doorbell, , sizeof(val)); +slot = find_first_bit(doorbell, nutrs); while (slot < nutrs) { req = >req_list[slot]; @@ -285,7 +285,7 @@ static void ufs_process_db(UfsHc *u, uint32_t val) trace_ufs_process_db(slot); req->state = UFS_REQUEST_READY; -slot = find_next_bit(, nutrs, slot + 1); +slot = find_next_bit(doorbell, nutrs, slot + 1); } qemu_bh_schedule(u->doorbell_bh); @@ -838,7 +838,7 @@ static QueryRespCode ufs_read_unit_desc(UfsRequest *req) uint8_t lun = req->req_upiu.qr.index; if (lun != UFS_UPIU_RPMB_WLUN && -(lun > UFS_MAX_LUS || u->lus[lun] == NULL)) { +(lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) { trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun); return UFS_QUERY_RESULT_INVALID_INDEX; } diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index ed3dbca154..15d467630c 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -497,7 +497,7 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpuint(block_size, ==, 4096); /* Write data */ -memset(write_buf, rand() % 255 + 1, block_size); +memset(write_buf, 0xab, block_size); ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, , _upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); -- 2.34.1
Re: [PULL 3/5] hw/ufs: Support for Query Transfer Requests
On 23. 9. 14. 23:40, Peter Maydell wrote: On Thu, 7 Sept 2023 at 19:17, Stefan Hajnoczi wrote: From: Jeuk Kim This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi Message-id: ff7a5f0fd26761936a553ffb89d3df0ba62844e9.1693980783.git.jeuk20@gmail.com Signed-off-by: Stefan Hajnoczi --- hw/ufs/ufs.h| 46 +++ hw/ufs/ufs.c| 988 +++- hw/ufs/trace-events | 1 + 3 files changed, 1033 insertions(+), 2 deletions(-) Hi; Coverity isn't happy about the code in this function (CID 1519050). The code isn't strictly wrong, but it's probably possible to make it a bit more clearly correct. +static void ufs_process_db(UfsHc *u, uint32_t val) +{ +unsigned long doorbell; +uint32_t slot; +uint32_t nutrs = u->params.nutrs; +UfsRequest *req; + +val &= ~u->reg.utrldbr; +if (!val) { +return; +} + +doorbell = val; +slot = find_first_bit(, nutrs); Here we pass the address of a single 'unsigned long' to find_first_bit(). That function operates on arrays, so unless nutrs is guaranteed to be less than 32 this might walk off the end of memory. There is a check on params.nutrs in ufs_check_constraints(), which checks for "> UFS_MAX_NUTRS" and that value is 32, so this won't actually overflow, but Coverity can't see that check and in any case what it really doesn't like here is the passing of the address of a 'long' variable to a function that is prototyped as taking an array of longs. You can probably make Coverity happy by defining doorbell here as a 1 element array, and asserting that nutrs is 32 or less. Alternatively, we have ctz32() for working through bits in a uint32_t, though that is a bit lower-level than find_first_bit/find_next_bit. + +while (slot < nutrs) { +req = >req_list[slot]; +if (req->state == UFS_REQUEST_ERROR) { +trace_ufs_err_utrl_slot_error(req->slot); +return; +} + +if (req->state != UFS_REQUEST_IDLE) { +trace_ufs_err_utrl_slot_busy(req->slot); +return; +} + +trace_ufs_process_db(slot); +req->state = UFS_REQUEST_READY; +slot = find_next_bit(, nutrs, slot + 1); +} + +qemu_bh_schedule(u->doorbell_bh); +} thanks -- PMM Thank you for letting me know about the coverity issue with a detailed description! I have checked all the coverity issues related to ufs. (cid 1519042, cid 1519043, cid 1519050, cid 1519051) I will fix them with an additional patch as soon as possible. Thank you! Jeuk
Re: [PULL 4/5] hw/ufs: Support for UFS logical unit
On 23. 9. 15. 02:31, Paolo Bonzini wrote: On 9/7/23 20:16, Stefan Hajnoczi wrote: From: Jeuk Kim This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi Message-id:beacc504376ab6a14b1a3830bb3c69382cf6aebc.1693980783.git.jeuk20@gmail.com Signed-off-by: Stefan Hajnoczi --- Jeuk, can you explain the differences between scsi-hd and ufs-lu, apart from the different bus type? Ideally, the UFS controller would be in hw/scsi/ufs.c and there would be no need for ufs-lu at all. Would it make sense to allow any SCSI device into a UFS bus without the need to have duplicate code? Thanks! Paolo Hi Paolo, While ufs does use the SCSI specification to communicate with the driver, it doesn't behave exactly like a typical scsi device. First, ufs-lu has a feature called "unit descriptor". This feature shows the status of the ufs-lu and only works with UFS-specific "query request" commands, not SCSI commands. UFS also has something called a well-known lu. Unlike typical SCSI devices, where each lu is independent, UFS can control other lu's through the well-known lu. Finally, UFS-LU will have features that SCSI-HD does not have, such as the zone block command. In addition to this, I wanted some scsi commands to behave differently from scsi-hd, for example, the Inquiry command should read "QEMU UFS" instead of "QEMU HARDDISK", and the mode_sense_page command should have a different result. For these reasons, I chose to generate the ufs-lu code separately. Please let me know if you have any comments on this. Thanks! Jeuk
[PATCH v10 2/4] hw/ufs: Support for Query Transfer Requests
From: Jeuk Kim This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c| 988 +++- hw/ufs/ufs.h| 46 +++ 3 files changed, 1033 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d1badcad10..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -18,6 +18,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index df87f2a6d5..56a8ec286b 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,221 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy
[PATCH v10 1/4] hw/ufs: Initial commit for emulated Universal-Flash-Storage
From: Jeuk Kim Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 32 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 278 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1090 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1461 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 3b29568ed4..85cb0f261e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2248,6 +2248,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..d1badcad10 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,32 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, ui
[PATCH v10 4/4] tests/qtest: Introduce tests for UFS
From: Jeuk Kim This patch includes the following tests Test mmio read Test ufs device initialization and ufs-lu recognition Test I/O (Performs a write followed by a read to verify) Signed-off-by: Jeuk Kim Acked-by: Thomas Huth Reviewed-by: Stefan Hajnoczi --- MAINTAINERS | 1 + tests/qtest/meson.build | 1 + tests/qtest/ufs-test.c | 587 3 files changed, 589 insertions(+) create mode 100644 tests/qtest/ufs-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 85cb0f261e..84af034447 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2253,6 +2253,7 @@ M: Jeuk Kim S: Supported F: hw/ufs/* F: include/block/ufs.h +F: tests/qtest/ufs-test.c megasas M: Hannes Reinecke diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 3afe9e9ee3..8c08604e6d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -269,6 +269,7 @@ qos_test_ss.add( 'virtio-iommu-test.c', 'vmxnet3-test.c', 'igb-test.c', + 'ufs-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 00..ed3dbca154 --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,587 @@ +/* + * QTest testcase for UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "scsi/constants.h" +#include "include/block/ufs.h" + +/* Test images sizes in Bytes */ +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Timeout for various operations, in seconds. */ +#define TIMEOUT_SECONDS 10 +/* Maximum PRD entry count */ +#define MAX_PRD_ENTRY_COUNT 10 +#define PRD_ENTRY_DATA_SIZE 4096 +/* Constants to build upiu */ +#define UTP_COMMAND_DESCRIPTOR_SIZE 4096 +#define UTP_RESPONSE_UPIU_OFFSET 1024 +#define UTP_PRDT_UPIU_OFFSET 2048 + +typedef struct QUfs QUfs; + +struct QUfs { +QOSGraphObject obj; +QPCIDevice dev; +QPCIBar bar; + +uint64_t utrlba; +uint64_t utmrlba; +uint64_t cmd_desc_addr; +uint64_t data_buffer_addr; + +bool enabled; +}; + +static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) +{ +return qpci_io_readl(>dev, ufs->bar, offset); +} + +static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) +{ +qpci_io_writel(>dev, ufs->bar, offset, value); +} + +static void ufs_wait_for_irq(QUfs *ufs) +{ +uint64_t end_time; +uint32_t is; +/* Wait for device to reset as the linux driver does. */ +end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; +do { +qtest_clock_step(ufs->dev.bus->qts, 100); +is = ufs_rreg(ufs, A_IS); +} while (is == 0 && g_get_monotonic_time() < end_time); +} + +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, + uint8_t slot, + uint32_t data_direction, + uint16_t prd_table_length) +{ +UtpTransferReqDesc req = { 0 }; +uint64_t command_desc_base_addr = +cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + +req.header.dword_0 = +cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD); +req.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_COMMAND_STATUS); + +req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); +req.command_desc_base_addr_lo = +cpu_to_le32(command_desc_base_addr & 0x); +req.response_upiu_offset = +cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); +req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); +req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); +req.prd_table_length = cpu_to_le16(prd_table_length); +return req; +} + +static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, + UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +{ +/* Build up utp transfer request descriptor */ +UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, + UFS_UTP_NO_DATA_TRANSFER, 0); +uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); +uint64_t req_upiu_addr = +ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; +uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; +qtest_memwrite(ufs->dev.bus->qts, utrd_addr, , sizeof(utrd)); + +/* Build up request upiu */ +UtpUpiuReq req_upiu = { 0 }; +req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; +req_upiu.header.task_tag = slot; +qtest_memw
[PATCH v10 0/4] hw/ufs: Add Universal Flash Storage (UFS) support
Since v9: - Added the "UFS_" prefix to all define and enum defined in block/ufs.h. This fixes https://gitlab.com/qemu-project/qemu/-/jobs/4977255992 which is a win32 build error. - Fixed not to use pointer type casting (uint32_t * -> unsigned long *). It causes the bug in the find_first_bit() function on big endian host pc. This fixes https://gitlab.com/qemu-project/qemu/-/jobs/4977256030 which is qos-test failure on s390x hosts. Please let me know if there are any problems. Thank you very much! Jeuk Jeuk Kim (4): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit tests/qtest: Introduce tests for UFS MAINTAINERS |7 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1445 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1502 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1090 +++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + tests/qtest/meson.build |1 + tests/qtest/ufs-test.c | 587 +++ 18 files changed, 4835 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h create mode 100644 tests/qtest/ufs-test.c -- 2.34.1
[PATCH v10 3/4] hw/ufs: Support for UFS logical unit
From: Jeuk Kim This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- hw/ufs/lu.c | 1445 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1761 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..e1c46bddb1 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1445 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_M
Re: [PATCH v9 0/4] hw/ufs: Add Universal Flash Storage (UFS) support
On 23. 8. 30. 20:38, Stefan Hajnoczi wrote: On Thu, 3 Aug 2023 at 07:49, Jeuk Kim wrote: Dear Stefan, I'm really sorry, but could you please put this patch series instead of v8, which was previously merged into block-next? The fixes from v8 are below. Please let me know if you have any comments or issues. The CI hit a test failure: https://gitlab.com/qemu-project/qemu/-/jobs/4977256030 Please investigate how to fix this so this series can be merged. Thanks! Stefan https://gitlab.com/qemu-project/qemu/-/jobs/4977256030 https://gitlab.com/qemu-project/qemu/-/jobs/4977255992 I'll analyze and fix above issues and resend the patch. Thanks for letting me know. Jeuk
[PATCH v9 2/4] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c| 980 +++- hw/ufs/ufs.h| 46 +++ 3 files changed, 1025 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d1badcad10..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -18,6 +18,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 101082a8a3..c771f8e0b4 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,221 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +i
[PATCH v9 3/4] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- hw/ufs/lu.c | 1445 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1761 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ec8932ff86 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1445 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN;
[PATCH v9 1/4] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 32 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 278 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1090 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1461 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 6111b6b4d9..7da40dabc2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2256,6 +2256,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..d1badcad10 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,32 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t add
[PATCH v9 4/4] tests/qtest: Introduce tests for UFS
This patch includes the following tests Test mmio read Test ufs device initialization and ufs-lu recognition Test I/O (Performs a write followed by a read to verify) Signed-off-by: Jeuk Kim Acked-by: Thomas Huth Reviewed-by: Stefan Hajnoczi --- MAINTAINERS | 1 + tests/qtest/meson.build | 1 + tests/qtest/ufs-test.c | 584 3 files changed, 586 insertions(+) create mode 100644 tests/qtest/ufs-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 7da40dabc2..a45bac20c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2261,6 +2261,7 @@ M: Jeuk Kim S: Supported F: hw/ufs/* F: include/block/ufs.h +F: tests/qtest/ufs-test.c megasas M: Hannes Reinecke diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index b071d400b3..2b1d589a87 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -269,6 +269,7 @@ qos_test_ss.add( 'virtio-iommu-test.c', 'vmxnet3-test.c', 'igb-test.c', + 'ufs-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 00..34c97bdf1b --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,584 @@ +/* + * QTest testcase for UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "scsi/constants.h" +#include "include/block/ufs.h" + +/* Test images sizes in Bytes */ +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Timeout for various operations, in seconds. */ +#define TIMEOUT_SECONDS 10 +/* Maximum PRD entry count */ +#define MAX_PRD_ENTRY_COUNT 10 +#define PRD_ENTRY_DATA_SIZE 4096 +/* Constants to build upiu */ +#define UTP_COMMAND_DESCRIPTOR_SIZE 4096 +#define UTP_RESPONSE_UPIU_OFFSET 1024 +#define UTP_PRDT_UPIU_OFFSET 2048 + +typedef struct QUfs QUfs; + +struct QUfs { +QOSGraphObject obj; +QPCIDevice dev; +QPCIBar bar; + +uint64_t utrlba; +uint64_t utmrlba; +uint64_t cmd_desc_addr; +uint64_t data_buffer_addr; + +bool enabled; +}; + +static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) +{ +return qpci_io_readl(>dev, ufs->bar, offset); +} + +static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) +{ +qpci_io_writel(>dev, ufs->bar, offset, value); +} + +static void ufs_wait_for_irq(QUfs *ufs) +{ +uint64_t end_time; +uint32_t is; +/* Wait for device to reset as the linux driver does. */ +end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; +do { +qtest_clock_step(ufs->dev.bus->qts, 100); +is = ufs_rreg(ufs, A_IS); +} while (is == 0 && g_get_monotonic_time() < end_time); +} + +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, + uint8_t slot, + uint32_t data_direction, + uint16_t prd_table_length) +{ +UtpTransferReqDesc req = { 0 }; +uint64_t command_desc_base_addr = +cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + +req.header.dword_0 = +cpu_to_le32(1 << 28 | data_direction | UTP_REQ_DESC_INT_CMD); +req.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS); + +req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); +req.command_desc_base_addr_lo = +cpu_to_le32(command_desc_base_addr & 0x); +req.response_upiu_offset = +cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); +req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); +req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); +req.prd_table_length = cpu_to_le16(prd_table_length); +return req; +} + +static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, + UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +{ +/* Build up utp transfer request descriptor */ +UtpTransferReqDesc utrd = +ufs_build_req_utrd(ufs->cmd_desc_addr, slot, UTP_NO_DATA_TRANSFER, 0); +uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); +uint64_t req_upiu_addr = +ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; +uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; +qtest_memwrite(ufs->dev.bus->qts, utrd_addr, , sizeof(utrd)); + +/* Build up request upiu */ +UtpUpiuReq req_upiu = { 0 }; +req_upiu.header.trans_type = UPIU_TRANSACTION_NOP_OUT; +req_upiu.header.task_tag = slot; +qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, _upiu, +
[PATCH v9 0/4] hw/ufs: Add Universal Flash Storage (UFS) support
Dear Stefan, I'm really sorry, but could you please put this patch series instead of v8, which was previously merged into block-next? The fixes from v8 are below. Please let me know if you have any comments or issues. Thank you very much, Jeuk Since v8: - Fix compilation warnings (Mike Maslenkin reported. Thanks so much Mike!) - Skip ufs-test for qemu-system-ppc64 I have confirmed that the dma in ufs-test does not work well for qemu-system-ppc64. This seems to be because qemu-system-ppc64 is the only big-endian system test. Since there are currently no ufs devices supported on big-endian systems, I just skip the test for now, and leave it as a TODO. Jeuk Kim (4): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit tests/qtest: Introduce tests for UFS MAINTAINERS |7 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1445 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1494 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1090 +++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + tests/qtest/meson.build |1 + tests/qtest/ufs-test.c | 584 +++ 18 files changed, 4824 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h create mode 100644 tests/qtest/ufs-test.c -- 2.34.1
[PATCH v8 4/4] tests/qtest: Introduce tests for UFS
This patch includes the following tests Test mmio read Test ufs device initialization and ufs-lu recognition Test I/O (Performs a write followed by a read to verify) Signed-off-by: Jeuk Kim Acked-by: Thomas Huth Reviewed-by: Stefan Hajnoczi --- MAINTAINERS | 1 + tests/qtest/meson.build | 1 + tests/qtest/ufs-test.c | 573 3 files changed, 575 insertions(+) create mode 100644 tests/qtest/ufs-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 0c8a955b42..546f226e85 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2261,6 +2261,7 @@ M: Jeuk Kim S: Supported F: hw/ufs/* F: include/block/ufs.h +F: tests/qtest/ufs-test.c megasas M: Hannes Reinecke diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index b071d400b3..2b1d589a87 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -269,6 +269,7 @@ qos_test_ss.add( 'virtio-iommu-test.c', 'vmxnet3-test.c', 'igb-test.c', + 'ufs-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 00..226ba6d351 --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,573 @@ +/* + * QTest testcase for UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "scsi/constants.h" +#include "include/block/ufs.h" + +/* Test images sizes in Bytes */ +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Timeout for various operations, in seconds. */ +#define TIMEOUT_SECONDS 10 +/* Maximum PRD entry count */ +#define MAX_PRD_ENTRY_COUNT 10 +#define PRD_ENTRY_DATA_SIZE 4096 +/* Constants to build upiu */ +#define UTP_COMMAND_DESCRIPTOR_SIZE 4096 +#define UTP_RESPONSE_UPIU_OFFSET 1024 +#define UTP_PRDT_UPIU_OFFSET 2048 + +typedef struct QUfs QUfs; + +struct QUfs { +QOSGraphObject obj; +QPCIDevice dev; +QPCIBar bar; + +uint64_t utrlba; +uint64_t utmrlba; +uint64_t cmd_desc_addr; +uint64_t data_buffer_addr; + +bool enabled; +}; + +static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) +{ +return qpci_io_readl(>dev, ufs->bar, offset); +} + +static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) +{ +qpci_io_writel(>dev, ufs->bar, offset, value); +} + +static void ufs_wait_for_irq(QUfs *ufs) +{ +uint64_t end_time; +uint32_t is; +/* Wait for device to reset as the linux driver does. */ +end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; +do { +qtest_clock_step(ufs->dev.bus->qts, 100); +is = ufs_rreg(ufs, A_IS); +} while (is == 0 && g_get_monotonic_time() < end_time); +} + +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, + uint8_t slot, + uint32_t data_direction, + uint16_t prd_table_length) +{ +UtpTransferReqDesc req = { 0 }; +uint64_t command_desc_base_addr = +cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + +req.header.dword_0 = +cpu_to_le32(1 << 28 | data_direction | UTP_REQ_DESC_INT_CMD); +req.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS); + +req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); +req.command_desc_base_addr_lo = +cpu_to_le32(command_desc_base_addr & 0x); +req.response_upiu_offset = +cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); +req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); +req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); +req.prd_table_length = cpu_to_le16(prd_table_length); +return req; +} + +static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, + UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +{ +/* Build up utp transfer request descriptor */ +UtpTransferReqDesc utrd = +ufs_build_req_utrd(ufs->cmd_desc_addr, slot, UTP_NO_DATA_TRANSFER, 0); +uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); +uint64_t req_upiu_addr = +ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; +uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; +qtest_memwrite(ufs->dev.bus->qts, utrd_addr, , sizeof(utrd)); + +/* Build up request upiu */ +UtpUpiuReq req_upiu = { 0 }; +req_upiu.header.trans_type = UPIU_TRANSACTION_NOP_OUT; +req_upiu.header.task_tag = slot; +qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, _upiu, +
[PATCH v8 1/4] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 32 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 278 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1090 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1461 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 12e59b6b27..0c8a955b42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2256,6 +2256,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..d1badcad10 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,32 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t add
[PATCH v8 0/4] hw/ufs: Add Universal Flash Storage (UFS) support
Since v7: In ufs-test.c, make the following changes - Change TIMEOUT from 5 to 10 (Thomas's review comment) - Rename the temporary file to "qemu-ufs." (Thomas's review comment) - Use "-blockdev" instead of "-drive" (Stefan's review comment) Since v6: - Add tests/qtest/ufs-test.c to test ufs initialisation and I/O - Add struct UtpTaskReqDesc to include/block/ufs.h - Fix ufs_log2() logic - Fix ufs-lu to use 4K as default block size to match the ufs spec Since I created a new file, tests/qtest/ufs-test.c, I added Laurent Vivier to the cc list. Thank you. Since v5: - Fix to print an error message instead of a segmentation fault when no drive property is specified for a ufs-lu device Since v4: Addressed review comment from Stefan Hajnoczi. The fixes are as follows. - Keep u->reg fields in host endian (Removed little-endian helper functions from MemoryRegionOps) - Remove unnecessary NULL checks for g_new and g_malloc0 - Replace DEFINE_PROP_DRIVE_IOTHREAD -> DEFINE_PROP_DRIVE In case you were wondering, ufs and ufs-lu have been tested for the following behaviours. 1. Checked ufs device recognition in Windows10 environment 2. Verified ufs device recognition in Ubuntu 22.04 environment 3. Verified io behaviour via fio in Ubuntu 22.04 environment 4. Verified query request via ufs-tools in Ubuntu 22.04 environment Since v3: - Replace softmmu_ss -> system_ss in meson Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (4): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit tests/qtest: Introduce tests for UFS MAINTAINERS |7 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1445 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1494 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1090 +++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + tests/qtest/meson.build |1 + tests/qtest/ufs-test.c | 573 +++ 18 files changed, 4813 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h create mode 100644 tests/qtest/ufs-test.c -- 2.34.1
[PATCH v8 3/4] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- hw/ufs/lu.c | 1445 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1761 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ec8932ff86 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1445 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN;
[PATCH v8 2/4] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim Reviewed-by: Stefan Hajnoczi --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c| 980 +++- hw/ufs/ufs.h| 46 +++ 3 files changed, 1025 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d1badcad10..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -18,6 +18,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 101082a8a3..903418925c 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,221 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +i
Re: [PATCH v7 0/4] hw/ufs: Add Universal Flash Storage (UFS) support
On 7/27/2023 4:36 AM, Stefan Hajnoczi wrote: On Wed, Jul 26, 2023 at 02:30:49PM +0900, Jeuk Kim wrote: Since v6: - Add tests/qtest/ufs-test.c to test ufs initialisation and I/O - Add struct UtpTaskReqDesc to include/block/ufs.h - Fix ufs_log2() logic - Fix ufs-lu to use 4K as default block size to match the ufs spec Since I created a new file, tests/qtest/ufs-test.c, I added Laurent Vivier to the cc list. Thank you. Modulo the comments about the test case: Reviewed-by: Stefan Hajnoczi Thank you very much for your detailed reviews so far. I will resend the patch reflecting your comments on the test case. Thanks again! Jeuk
Re: [PATCH v7 4/4] tests/qtest: Introduce tests for UFS
On 7/26/2023 4:21 PM, Thomas Huth wrote: Hi! On 26/07/2023 07.30, Jeuk Kim wrote: This patch includes the following tests Test mmio read Test ufs device initialization and ufs-lu recognition Test I/O (Performs a write followed by a read to verify) Signed-off-by: Jeuk Kim --- ... diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 00..5104a0a56a --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,575 @@ +/* + * QTest testcase for UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "scsi/constants.h" +#include "include/block/ufs.h" + +/* Test images sizes in Bytes */ +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Timeout for various operations, in seconds. */ +#define TIMEOUT_SECONDS 5 From what I've seen in the past, it's possible that a process gets paused for 3 - 4 seconds on a very loaded CI machine, so 5 seconds is already close ... I'd suggest to use 8 - 10 seconds for a timeout instead, just to be on the safe side. +static char *drive_create(void) +{ + int fd, ret; + char *t_path; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("qtest.XX", _path, NULL); Could you maybe use "qtest-ufs.XX" or something more prominent instead? ... in case the files don't get deleted correctly, it's easier to know at which test to look at later. With that change: Acked-by: Thomas Huth Okay, I'm going to change the timeout to 10 and rename the file to "qtest-ufs.XX". Thank you for your comment!!
[PATCH v7 4/4] tests/qtest: Introduce tests for UFS
This patch includes the following tests Test mmio read Test ufs device initialization and ufs-lu recognition Test I/O (Performs a write followed by a read to verify) Signed-off-by: Jeuk Kim --- MAINTAINERS | 1 + tests/qtest/meson.build | 1 + tests/qtest/ufs-test.c | 575 3 files changed, 577 insertions(+) create mode 100644 tests/qtest/ufs-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 0c8a955b42..546f226e85 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2261,6 +2261,7 @@ M: Jeuk Kim S: Supported F: hw/ufs/* F: include/block/ufs.h +F: tests/qtest/ufs-test.c megasas M: Hannes Reinecke diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index b071d400b3..2b1d589a87 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -269,6 +269,7 @@ qos_test_ss.add( 'virtio-iommu-test.c', 'vmxnet3-test.c', 'igb-test.c', + 'ufs-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 00..5104a0a56a --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,575 @@ +/* + * QTest testcase for UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "scsi/constants.h" +#include "include/block/ufs.h" + +/* Test images sizes in Bytes */ +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Timeout for various operations, in seconds. */ +#define TIMEOUT_SECONDS 5 +/* Maximum PRD entry count */ +#define MAX_PRD_ENTRY_COUNT 10 +#define PRD_ENTRY_DATA_SIZE 4096 +/* Constants to build upiu */ +#define UTP_COMMAND_DESCRIPTOR_SIZE 4096 +#define UTP_RESPONSE_UPIU_OFFSET 1024 +#define UTP_PRDT_UPIU_OFFSET 2048 + +typedef struct QUfs QUfs; + +struct QUfs { +QOSGraphObject obj; +QPCIDevice dev; +QPCIBar bar; + +uint64_t utrlba; +uint64_t utmrlba; +uint64_t cmd_desc_addr; +uint64_t data_buffer_addr; + +bool enabled; +}; + +static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) +{ +return qpci_io_readl(>dev, ufs->bar, offset); +} + +static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) +{ +qpci_io_writel(>dev, ufs->bar, offset, value); +} + +static void ufs_wait_for_irq(QUfs *ufs) +{ +uint64_t end_time; +uint32_t is; +/* Wait for device to reset as the linux driver does. */ +end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; +do { +qtest_clock_step(ufs->dev.bus->qts, 100); +is = ufs_rreg(ufs, A_IS); +} while (is == 0 && g_get_monotonic_time() < end_time); +} + +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, + uint8_t slot, + uint32_t data_direction, + uint16_t prd_table_length) +{ +UtpTransferReqDesc req = { 0 }; +uint64_t command_desc_base_addr = +cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + +req.header.dword_0 = +cpu_to_le32(1 << 28 | data_direction | UTP_REQ_DESC_INT_CMD); +req.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS); + +req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); +req.command_desc_base_addr_lo = +cpu_to_le32(command_desc_base_addr & 0x); +req.response_upiu_offset = +cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); +req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); +req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); +req.prd_table_length = cpu_to_le16(prd_table_length); +return req; +} + +static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, + UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +{ +/* Build up utp transfer request descriptor */ +UtpTransferReqDesc utrd = +ufs_build_req_utrd(ufs->cmd_desc_addr, slot, UTP_NO_DATA_TRANSFER, 0); +uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); +uint64_t req_upiu_addr = +ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; +uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; +qtest_memwrite(ufs->dev.bus->qts, utrd_addr, , sizeof(utrd)); + +/* Build up request upiu */ +UtpUpiuReq req_upiu = { 0 }; +req_upiu.header.trans_type = UPIU_TRANSACTION_NOP_OUT; +req_upiu.header.task_tag = slot; +qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, _upiu, + sizeof(req_upiu)); + +/* Ring Doorbell */ +
[PATCH v7 1/4] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 32 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 278 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1090 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1461 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 12e59b6b27..0c8a955b42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2256,6 +2256,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..d1badcad10 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,32 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slo
[PATCH v7 3/4] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1445 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1761 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ec8932ff86 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1445 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN; +} + +if (is_wlun(req
[PATCH v7 2/4] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c| 980 +++- hw/ufs/ufs.h| 46 +++ 3 files changed, 1025 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d1badcad10..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -18,6 +18,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 101082a8a3..903418925c 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,221 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +if (ret) { +trace_uf
[PATCH v7 0/4] hw/ufs: Add Universal Flash Storage (UFS) support
Since v6: - Add tests/qtest/ufs-test.c to test ufs initialisation and I/O - Add struct UtpTaskReqDesc to include/block/ufs.h - Fix ufs_log2() logic - Fix ufs-lu to use 4K as default block size to match the ufs spec Since I created a new file, tests/qtest/ufs-test.c, I added Laurent Vivier to the cc list. Thank you. Since v5: - Fix to print an error message instead of a segmentation fault when no drive property is specified for a ufs-lu device Since v4: Addressed review comment from Stefan Hajnoczi. The fixes are as follows. - Keep u->reg fields in host endian (Removed little-endian helper functions from MemoryRegionOps) - Remove unnecessary NULL checks for g_new and g_malloc0 - Replace DEFINE_PROP_DRIVE_IOTHREAD -> DEFINE_PROP_DRIVE In case you were wondering, ufs and ufs-lu have been tested for the following behaviours. 1. Checked ufs device recognition in Windows10 environment 2. Verified ufs device recognition in Ubuntu 22.04 environment 3. Verified io behaviour via fio in Ubuntu 22.04 environment 4. Verified query request via ufs-tools in Ubuntu 22.04 environment Since v3: - Replace softmmu_ss -> system_ss in meson Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (4): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit tests/qtest: Introduce tests for UFS MAINTAINERS |7 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1445 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1494 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1090 +++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + tests/qtest/meson.build |1 + tests/qtest/ufs-test.c | 575 +++ 18 files changed, 4815 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h create mode 100644 tests/qtest/ufs-test.c -- 2.34.1
Re: [PATCH v6 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
On 7/21/2023 8:01 PM, Stefan Hajnoczi wrote: Is there a test case along the lines of tests/qtest/ahci-test.c that exercises this new code? It's important to have at least a basic test that initializes the device and performs a write followed by a read to verify that I/O is working. This will allow the CI system and anyone who modifies the UFS code to check that the basics are still working. Thanks, Stefan Okay, I'll prepare patch v7 by adding tests related to ufs initialisation and io tests. Thanks, Jeuk
[PATCH v6 2/3] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c| 980 +++- hw/ufs/ufs.h| 46 +++ 3 files changed, 1025 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d1badcad10..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -18,6 +18,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 101082a8a3..cd33af2cde 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,221 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +if (ret) { +trace_uf
[PATCH v6 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
Since v5: - Fix to print an error message instead of a segmentation fault when no drive property is specified for a ufs-lu device Since v4: Addressed review comment from Stefan Hajnoczi. The fixes are as follows. - Keep u->reg fields in host endian (Removed little-endian helper functions from MemoryRegionOps) - Remove unnecessary NULL checks for g_new and g_malloc0 - Replace DEFINE_PROP_DRIVE_IOTHREAD -> DEFINE_PROP_DRIVE In case you were wondering, ufs and ufs-lu have been tested for the following behaviours. 1. Checked ufs device recognition in Windows10 environment 2. Verified ufs device recognition in Ubuntu 22.04 environment 3. Verified io behaviour via fio in Ubuntu 22.04 environment 4. Verified query request via ufs-tools in Ubuntu 22.04 environment Since v3: - Replace softmmu_ss -> system_ss in meson Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1439 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1494 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 16 files changed, 4190 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h -- 2.34.1
[PATCH v6 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 32 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 278 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1419 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 12e59b6b27..0c8a955b42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2256,6 +2256,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..d1badcad10 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,32 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slo
[PATCH v6 3/3] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1439 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1755 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..6f6d301bc7 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1439 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN; +} + +if (is_wlun(req
Re: [PATCH v5 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
On 7/21/2023 3:49 AM, Stefan Hajnoczi wrote: Hi, I'm ready to merge this but encountered a bug when testing: $ qemu-system-x86_64 --device ufs --device ufs-lu Segmentation fault (core dumped) Please ensure there is an error message like with SCSI disks: $ qemu-system-x86_64 --device virtio-scsi-pci --device scsi-hd qemu-system-x86_64: --device scsi-hd: drive property not set Thanks, Stefan Thanks for letting me know. I'll fix it right away and send the patch again. Thanks, Jeuk
[PATCH v5 3/3] hw/ufs: Support for UFS logical unit
From: Jeuk Kim This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1439 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1755 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ab40f42190 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1439 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN;
[PATCH v5 2/3] hw/ufs: Support for Query Transfer Requests
From: Jeuk Kim This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/trace-events | 1 + hw/ufs/ufs.c| 980 +++- hw/ufs/ufs.h| 46 +++ 3 files changed, 1025 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index d1badcad10..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -18,6 +18,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 101082a8a3..cd33af2cde 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,221 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +if (ret) { +trac
[PATCH v5 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
From: Jeuk Kim Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 32 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 278 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1419 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 12e59b6b27..0c8a955b42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2256,6 +2256,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..d1badcad10 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,32 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t add
[PATCH v5 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
Since v4: Addressed review comment from Stefan Hajnoczi. The fixes are as follows. - Keep u->reg fields in host endian (Removed little-endian helper functions from MemoryRegionOps) - Remove unnecessary NULL checks for g_new and g_malloc0 - Replace DEFINE_PROP_DRIVE_IOTHREAD -> DEFINE_PROP_DRIVE In case you were wondering, ufs and ufs-lu have been tested for the following behaviours. 1. Checked ufs device recognition in Windows10 environment 2. Verified ufs device recognition in Ubuntu 22.04 environment 3. Verified io behaviour via fio in Ubuntu 22.04 environment 4. Verified query request via ufs-tools in Ubuntu 22.04 environment Since v3: - Replace softmmu_ss -> system_ss in meson Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1439 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1494 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 16 files changed, 4190 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h -- 2.34.1
Re: PING: [PATCH v4 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
On 2023-07-19 오전 3:56, Stefan Hajnoczi wrote: On Tue, Jul 11, 2023 at 07:31:02PM +0900, Jeuk Kim wrote: Hi, Any more reviews...? Dear Stefan If you don't mind, Could you give it "reviewed-by"? And is there anything else I should do...? Sorry for the late reply. I was on vacation and am working my way through pending code review and bug reports. I have started reviewing this series and should be finish on Wednesday or Thursday. Stefan Thank you for your review. I have seen your comments on PATCH v4. I'll send you a patch v5 as soon as I fix the things you commented on. Thanks again. Jeuk
PING: [PATCH v4 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
Hi, Any more reviews...? Dear Stefan If you don't mind, Could you give it "reviewed-by"? And is there anything else I should do...? Thanks, Jeuk On 2023-07-04 오후 5:33, Jeuk Kim wrote: From: Jeuk Kim Since v3: - Replace softmmu_ss -> system_ss in meson Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1441 +++ hw/ufs/meson.build |1 + hw/ufs/trace-events | 59 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1545 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 16 files changed, 4244 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h
[PATCH v4 3/3] hw/ufs: Support for UFS logical unit
From: Jeuk Kim This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1441 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1757 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ef69de61a5 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1441 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN;
[PATCH v4 2/3] hw/ufs: Support for Query Transfer Requests
From: Jeuk Kim This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/trace-events |1 + hw/ufs/ufs.c| 1005 ++- hw/ufs/ufs.h| 46 ++ 3 files changed, 1050 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 17793929b1..97cf6664b9 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -19,6 +19,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 99e5e55e97..2b564141c6 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,234 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +uint32_t utrlba = ldl_le_p(>reg.utrlba); +uint32_t utrlbau = ldl_le_p(>reg.utrlbau); +hwaddr utrl_base_addr = (((hwaddr)utrlbau) << 32) + utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE
[PATCH v4 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
From: Jeuk Kim Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 33 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 304 +++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1446 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 4feea49a6e..756aae8623 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2237,6 +2237,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..eb5164bde9 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..17793929b1 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,33 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_memory_allocation(void) "failed to allocate memory" +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
[PATCH v4 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
From: Jeuk Kim Since v3: - Replace softmmu_ss -> system_ss in meson Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1441 +++ hw/ufs/meson.build |1 + hw/ufs/trace-events | 59 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1545 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 16 files changed, 4244 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h -- 2.34.1
[PATCH v3 3/3] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1441 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1757 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ef69de61a5 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1441 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN; +} + +if (is_wlun(req
[PATCH v3 2/3] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/trace-events |1 + hw/ufs/ufs.c| 1005 ++- hw/ufs/ufs.h| 46 ++ 3 files changed, 1050 insertions(+), 2 deletions(-) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 17793929b1..97cf6664b9 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -19,6 +19,7 @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 99e5e55e97..2b564141c6 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -15,10 +15,234 @@ #include "ufs.h" /* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +#define UFS_SPEC_VER 0x0310 #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +uint32_t utrlba = ldl_le_p(>reg.utrlba); +uint32_t utrlbau = ldl_le_p(>reg.utrlbau); +hwaddr utrl_base_addr = (((hwaddr)utrlbau) << 32) + utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +
[PATCH v3 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 33 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 304 +++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 14 files changed, 1446 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 88b5a7ee0a..91c2bfbb09 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2231,6 +2231,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index e302bea484..d6707fa069 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -92,6 +92,8 @@ PCI devices (other than virtio): PCI PVPanic device (``-device pvpanic-pci``) 1b36:0012 PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) All these devices are documented in :doc:`index`. diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..c1d90eeea6 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..17793929b1 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,33 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_memory_allocation(void) "failed to allocate memory" +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_
[PATCH v3 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
Since v2: Addressed review comment from Stefan Hajnoczi. The main fixes are as follows. - Use of SPDX licence identifiers - fixed endianness error - removed memory leak - fixed DMA error handling logic This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + docs/specs/pci-ids.rst |2 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1441 +++ hw/ufs/meson.build |1 + hw/ufs/trace-events | 59 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1545 ++ hw/ufs/ufs.h | 131 include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 16 files changed, 4244 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h -- 2.34.1
Re: [PATCH v2 3/3] hw/ufs: Support for UFS logical unit
On Fri, Jun 19, 2023, Stefan Hajnoczi wrote: >On Fri, Jun 16, 2023 at 03:58:27PM +0900, Jeuk Kim wrote: >> This commit adds support for ufs logical unit. >> The LU handles processing for the SCSI command, >> unit descriptor query request. >> >> This commit enables the UFS device to process >> IO requests. > >Is UFS a SCSI Host Bus Adapter capable of exposing any SCSI device? The >code is written as if UFS was a special-purpose SCSI bus that cannot >handle regular SCSI devices already emulated by QEMU (like scsi-hd). As >a result, it duplicates a lot of SCSI device code instead of just >focussing on unwrapping/wrapping the SCSI commands and responses from >the UFS interface. > >Would it be possible to have: > > --device ufs,id= > --device scsi-hd,bus= > >? > >I think that would involve less code and be more flexible. > Unfortunately, UFS is not a generic SCSI Host Bus Adapter. UFS uses the SCSI specification to communicate with the driver, but its behaviour is very different from that of a typical SCSI device. (So it's intentional that UFS looks like a special-purpose SCSI bus.) For example, UFS has the well-known lu. Unlike typical SCSI devices, where each lu is independent, UFS can control other lu's through the well-known lu. Therefore, UFS can only work properly with ufs-lu, and not with other scsi devices such as scsi-hd. :'( That's why I made the UFS bus and added the ufs_bus_check_address() to prevent normal scsi devices and UFS from connecting to each other. Also, in the future, I will add more ufs-specific features like hibernation and zoned, which are different from normal SCSI devices. So personally, I think we should define ufs-lu separately as we do now. Is that okay? >> >> Signed-off-by: Jeuk Kim >> --- >> hw/ufs/lu.c | 1441 ++ >> hw/ufs/meson.build |2 +- >> hw/ufs/trace-events | 25 + >> hw/ufs/ufs.c | 252 ++- >> hw/ufs/ufs.h | 43 ++ >> include/scsi/constants.h |1 + >> 6 files changed, 1757 insertions(+), 7 deletions(-) >> create mode 100644 hw/ufs/lu.c >> >> diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c >> new file mode 100644 >> index 00..ef69de61a5 >> --- /dev/null >> +++ b/hw/ufs/lu.c >> @@ -0,0 +1,1441 @@ >> +/* >> + * QEMU UFS Logical Unit >> + * >> + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. >> + * >> + * Written by Jeuk Kim >> + * >> + * This code is licensed under the GNU GPL v2 or later. >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qemu/units.h" >> +#include "qapi/error.h" >> +#include "qemu/memalign.h" >> +#include "hw/scsi/scsi.h" >> +#include "scsi/constants.h" >> +#include "sysemu/block-backend.h" >> +#include "qemu/cutils.h" >> +#include "trace.h" >> +#include "ufs.h" >> + >> +/* >> + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, >> + * with minor adjustments to make it work for UFS. >> + */ >> + >> +#define SCSI_DMA_BUF_SIZE (128 * KiB) >> +#define SCSI_MAX_INQUIRY_LEN 256 >> +#define SCSI_INQUIRY_DATA_SIZE 36 >> +#define SCSI_MAX_MODE_LEN 256 >> + >> +typedef struct UfsSCSIReq { >> +SCSIRequest req; >> +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. >> */ >> +uint64_t sector; >> +uint32_t sector_count; >> +uint32_t buflen; >> +bool started; >> +bool need_fua_emulation; >> +struct iovec iov; >> +QEMUIOVector qiov; >> +BlockAcctCookie acct; >> +} UfsSCSIReq; >> + >> +static void ufs_scsi_free_request(SCSIRequest *req) >> +{ >> +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); >> + >> +qemu_vfree(r->iov.iov_base); >> +} >> + >> +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) >> +{ >> +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, >> + sense.ascq); >> +scsi_req_build_sense(>req, sense); >> +scsi_req_complete(>req, CHECK_CONDITION); >> +} >> + >> +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, >> + uint32_t outbuf_len) >> +{ >> +UfsHc *u = UFS(req->bus->qbus.parent); >> +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); >> +uint8_t page_code = req->cmd.buf[2];
Re: [PATCH v2 2/3] hw/ufs: Support for Query Transfer Requests
On Fri, Jun 19, 2023, Stefan Hajnoczi wrote: >On Fri, Jun 16, 2023 at 03:58:25PM +0900, Jeuk Kim wrote: >> This commit makes the UFS device support query >> and nop out transfer requests. >> >> The next patch would be support for UFS logical >> unit and scsi command transfer request. >> >> Signed-off-by: Jeuk Kim >> --- >> hw/ufs/ufs.c | 968 ++- >> hw/ufs/ufs.h | 45 +++ >> 2 files changed, 1012 insertions(+), 1 deletion(-) >> >> diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c >> index 9dba1073a8..10ecc8cd7b 100644 >> --- a/hw/ufs/ufs.c >> +++ b/hw/ufs/ufs.c >> @@ -19,6 +19,233 @@ >> #define UFS_MAX_NUTRS 32 >> #define UFS_MAX_NUTMRS 8 >> >> +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) >> +{ >> +uint32_t cap = ldl_le_p(>reg.cap); >> +hwaddr hi = addr + size - 1; >> + >> +if (hi < addr) { >> +return MEMTX_DECODE_ERROR; >> +} >> + >> +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { >> +return MEMTX_DECODE_ERROR; >> +} >> + >> +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); >> +} >> + >> +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, >> + int size) >> +{ >> +uint32_t cap = ldl_le_p(>reg.cap); >> +hwaddr hi = addr + size - 1; >> +if (hi < addr) { >> +return MEMTX_DECODE_ERROR; >> +} >> + >> +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { >> +return MEMTX_DECODE_ERROR; >> +} >> + >> +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); >> +} >> + >> +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); >> + >> +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) >> +{ >> +uint32_t utrlba = ldl_le_p(>reg.utrlba); >> +uint32_t utrlbau = ldl_le_p(>reg.utrlbau); >> +hwaddr utrl_base_addr = (((hwaddr)utrlbau) << 32) + utrlba; >> +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); >> + >> +return utrd_addr; >> +} >> + >> +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc >> *utrd) >> +{ >> +uint32_t cmd_desc_base_addr_lo = >> +le32_to_cpu(utrd->command_desc_base_addr_lo); >> +uint32_t cmd_desc_base_addr_hi = >> +le32_to_cpu(utrd->command_desc_base_addr_hi); >> + >> +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; >> +} >> + >> +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc >> *utrd) >> +{ >> +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); >> +uint32_t rsp_upiu_byte_off = >> +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); >> +return req_upiu_base_addr + rsp_upiu_byte_off; >> +} >> + >> +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) >> +{ >> +UfsHc *u = req->hc; >> +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); >> +MemTxResult ret; >> + >> +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); >> +if (ret) { >> +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); >> +} >> +return ret; >> +} >> + >> +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) >> +{ >> +UfsHc *u = req->hc; >> +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); >> +UtpUpiuReq *req_upiu = >req_upiu; >> +uint32_t copy_size; >> +uint16_t data_segment_length; >> +MemTxResult ret; >> + >> +/* >> + * To know the size of the req_upiu, we need to read the >> + * data_segment_length in the header first. >> + */ >> +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, >> +sizeof(UtpUpiuHeader)); >> +if (ret) { >> +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); >> +return ret; >> +} >> +data_segment_length = >> be16_to_cpu(req_upiu->https://protect2.fireeye.com/v1/url?k=62228ed9-3dbea7f3-62230596-000babe598f7-b6885126c3989767=1=d20f362c-c592-4425-86c7-2f65b07a0d0e=http%3A%2F%2Fheader.data%2F_segment_length); >> + >> +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE >>
Re: [PATCH v2 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
On Fri, Jun 16, 2023, Stefan Hajnoczi wrote: >On Fri, Jun 16, 2023 at 03:58:21PM +0900, Jeuk Kim wrote: >> Universal Flash Storage (UFS) is a high-performance mass storage device >> with a serial interface. It is primarily used as a high-performance >> data storage device for embedded applications. >> >> This commit contains code for UFS device to be recognized >> as a UFS PCI device. >> Patches to handle UFS logical unit and Transfer Request will follow. >> >> Signed-off-by: Jeuk Kim >> --- >> MAINTAINERS |6 + >> hw/Kconfig |1 + >> hw/meson.build |1 + >> hw/ufs/Kconfig |4 + >> hw/ufs/meson.build |1 + >> hw/ufs/trace-events | 33 ++ >> hw/ufs/trace.h |1 + >> hw/ufs/ufs.c | 305 +++ >> hw/ufs/ufs.h | 42 ++ >> include/block/ufs.h | 1048 ++ >> include/hw/pci/pci.h |1 + >> include/hw/pci/pci_ids.h |1 + >> meson.build |1 + >> 13 files changed, 1445 insertions(+) >> create mode 100644 hw/ufs/Kconfig >> create mode 100644 hw/ufs/meson.build >> create mode 100644 hw/ufs/trace-events >> create mode 100644 hw/ufs/trace.h >> create mode 100644 hw/ufs/ufs.c >> create mode 100644 hw/ufs/ufs.h >> create mode 100644 include/block/ufs.h >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 88b5a7ee0a..91c2bfbb09 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -2231,6 +2231,12 @@ F: tests/qtest/nvme-test.c >> F: docs/system/devices/nvme.rst >> T: git git://git.infradead.org/qemu-nvme.git nvme-next >> >> +ufs >> +M: Jeuk Kim >> +S: Supported >> +F: hw/ufs/* >> +F: include/block/ufs.h > >Thank you for stepping up as maintainer for UFS. The responsibilities of >maintainers are to: > >1. Review other people's patches that modify the code. >2. Send pull requests to the qemu.git maintainer or post your > acknowledgement of patches so another maintainer can merge patches > into a larger subsystem branch. For example, you could ack patches > and Klaus could include them in his pull requests for the time being. >3. Ensure a basic level of testing and CI integration to prevent > bitrot and regressions. > >For this last point, I suggest writing a libqos test that performs >device initialization and executes some basic I/O. Not all existing >emulated storage controllers have this level of testing, but I highly >recommend having an automated test case for your new device. There is >documentation on how to write tests here: >https://qemu.readthedocs.io/en/latest/devel/qtest.html > >An example test case is the AHCI (SATA) controller test: >https://gitlab.com/qemu-project/qemu/-/blob/master/tests/qtest/ahci-test.c > I'll keep the maintainer's responsibilities in mind. I'll prepare the libqos test in the next patch. Thank you!! >> megasas >> M: Hannes Reinecke >> L: qemu-block@nongnu.org >> diff --git a/hw/Kconfig b/hw/Kconfig >> index ba62ff6417..9ca7b38c31 100644 >> --- a/hw/Kconfig >> +++ b/hw/Kconfig >> @@ -38,6 +38,7 @@ source smbios/Kconfig >> source ssi/Kconfig >> source timer/Kconfig >> source tpm/Kconfig >> +source ufs/Kconfig >> source usb/Kconfig >> source virtio/Kconfig >> source vfio/Kconfig >> diff --git a/hw/meson.build b/hw/meson.build >> index c7ac7d3d75..f01fac4617 100644 >> --- a/hw/meson.build >> +++ b/hw/meson.build >> @@ -37,6 +37,7 @@ subdir('smbios') >> subdir('ssi') >> subdir('timer') >> subdir('tpm') >> +subdir('ufs') >> subdir('usb') >> subdir('vfio') >> subdir('virtio') >> diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig >> new file mode 100644 >> index 00..b7b3392e85 >> --- /dev/null >> +++ b/hw/ufs/Kconfig >> @@ -0,0 +1,4 @@ >> +config UFS_PCI >> +bool >> +default y if PCI_DEVICES >> +depends on PCI >> diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build >> new file mode 100644 >> index 00..c1d90eeea6 >> --- /dev/null >> +++ b/hw/ufs/meson.build >> @@ -0,0 +1 @@ >> +softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) >> diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events >> new file mode 100644 >> index 00..17793929b1 >> --- /dev/null >> +++ b/hw/ufs/trace-events >> @@ -0,0 +1,33 @@ >> +# ufs.c >> +ufs_irq_raise(void) "INTx" >> +ufs_
[PATCH v2 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
Since v1: - use macros of "hw/registerfields.h" (Addressed Philippe's review comments) This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1441 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1511 ++ hw/ufs/ufs.h | 130 include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 15 files changed, 4206 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h -- 2.34.1
[PATCH v2 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 33 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 305 +++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1048 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 13 files changed, 1445 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 88b5a7ee0a..91c2bfbb09 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2231,6 +2231,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..c1d90eeea6 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..17793929b1 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,33 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_memory_allocation(void) "failed to allocate memory" +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" +ufs_err_unsupport_register_offset(uint32_t offset)
[PATCH v2 3/3] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1441 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 252 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1757 insertions(+), 7 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ef69de61a5 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1441 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN; +} + +if (is_wlun(req
[PATCH v2 2/3] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 968 ++- hw/ufs/ufs.h | 45 +++ 2 files changed, 1012 insertions(+), 1 deletion(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 9dba1073a8..10ecc8cd7b 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -19,6 +19,233 @@ #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!FIELD_EX32(cap, CAP, 64AS) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +uint32_t utrlba = ldl_le_p(>reg.utrlba); +uint32_t utrlbau = ldl_le_p(>reg.utrlbau); +hwaddr utrl_base_addr = (((hwaddr)utrlbau) << 32) + utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_prdt(UfsRequest *req) +{ +UfsHc *u = req->hc; +uint16_t prdt_len = le16_to_cpu(req->utrd.prd_table_length); +uint16_t prdt_byte_off = +le16_to_cpu(req->utrd.prd_table_offset) * sizeof(uint32_t); +uint32_t prdt_size = prdt_len * sizeof(UfshcdSgEntry); +UfshcdSgEntry *prd_entries; +hwaddr req_upiu_base_addr, prdt_base_addr; +int err; + +assert(!req->sg); + +if (prdt_len == 0) { +return MEMTX_OK; +} + +prd_entries = g_new(UfshcdSgEntry, prdt_size); +if (!prd_entries) { +trace_ufs_err_memory_allocation(); +return MEMTX_ERROR; +} + +req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +prdt_base_addr = req_upiu_base_addr + prdt_byte_off; + +err = ufs_addr_read(u, prdt_base_addr, prd_entries, prdt_size); +if (err) { +trace_ufs_err_dma_read_prdt(req->slot, prdt_base_addr); +return err; +} + +req->sg = g_malloc0(sizeof(QEMUSGList)); +if (!req->sg) { +trace_ufs_err_memory_allocation(); +g_free(prd_entri
Re: [PATCH 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Hi Philippe, >Hi Jeuk, > >[+Alistair] > >On 26/5/23 07:05, Jeuk Kim wrote: >> Universal Flash Storage (UFS) is a high-performance mass storage device >> with a serial interface. It is primarily used as a high-performance >> data storage device for embedded applications. >> >> This commit contains code for UFS device to be recognized >> as a UFS PCI device. >> Patches to handle UFS logical unit and Transfer Request will follow. >> >> Signed-off-by: Jeuk Kim >> --- >> MAINTAINERS |6 + >> hw/Kconfig |1 + >> hw/meson.build |1 + >> hw/ufs/Kconfig |4 + >> hw/ufs/meson.build |1 + >> hw/ufs/trace-events | 33 + >> hw/ufs/trace.h |1 + >> hw/ufs/ufs.c | 305 ++ >> hw/ufs/ufs.h | 42 ++ >> include/block/ufs.h | 1251 ++ >> include/hw/pci/pci.h |1 + >> include/hw/pci/pci_ids.h |1 + >> meson.build |1 + >> 13 files changed, 1648 insertions(+) >> create mode 100644 hw/ufs/Kconfig >> create mode 100644 hw/ufs/meson.build >> create mode 100644 hw/ufs/trace-events >> create mode 100644 hw/ufs/trace.h >> create mode 100644 hw/ufs/ufs.c >> create mode 100644 hw/ufs/ufs.h >> create mode 100644 include/block/ufs.h > > >> diff --git a/include/block/ufs.h b/include/block/ufs.h >> new file mode 100644 >> index 00..0ce3a19bc0 >> --- /dev/null >> +++ b/include/block/ufs.h >> @@ -0,0 +1,1251 @@ >> +#ifndef BLOCK_UFS_H >> +#define BLOCK_UFS_H >> + >> +#include "hw/registerfields.h" > >Since you use the registerfields API, ... > >> +typedef struct QEMU_PACKED UfsReg { >> +uint32_t cap; >> +uint32_t rsvd0; >> +uint32_t ver; >> +uint32_t rsvd1; >> +uint32_t hcpid; >> +uint32_t hcmid; >> +uint32_t ahit; >> +uint32_t rsvd2; >> +uint32_t is; >> +uint32_t ie; >> +uint32_t rsvd3[2]; >> +uint32_t hcs; >> +uint32_t hce; >> +uint32_t uecpa; >> +uint32_t uecdl; >> +uint32_t uecn; >> +uint32_t uect; >> +uint32_t uecdme; >> +uint32_t utriacr; >> +uint32_t utrlba; >> +uint32_t utrlbau; >> +uint32_t utrldbr; >> +uint32_t utrlclr; >> +uint32_t utrlrsr; >> +uint32_t utrlcnr; >> +uint32_t rsvd4[2]; >> +uint32_t utmrlba; >> +uint32_t utmrlbau; >> +uint32_t utmrldbr; >> +uint32_t utmrlclr; >> +uint32_t utmrlrsr; >> +uint32_t rsvd5[3]; >> +uint32_t uiccmd; >> +uint32_t ucmdarg1; >> +uint32_t ucmdarg2; >> +uint32_t ucmdarg3; >> +uint32_t rsvd6[4]; >> +uint32_t rsvd7[4]; >> +uint32_t rsvd8[16]; >> +uint32_t ccap; >> +} UfsReg; >> + >> +enum UfsRegOfs { >> +UFS_REG_CAP = offsetof(UfsReg, cap), >> +UFS_REG_VER = offsetof(UfsReg, ver), >> +UFS_REG_HCPID = offsetof(UfsReg, hcpid), >> +UFS_REG_HCMID = offsetof(UfsReg, hcmid), >> +UFS_REG_AHIT = offsetof(UfsReg, ahit), >> +UFS_REG_IS = offsetof(UfsReg, is), >> +UFS_REG_IE = offsetof(UfsReg, ie), >> +UFS_REG_HCS = offsetof(UfsReg, hcs), >> +UFS_REG_HCE = offsetof(UfsReg, hce), >> +UFS_REG_UECPA = offsetof(UfsReg, uecpa), >> +UFS_REG_UECDL = offsetof(UfsReg, uecdl), >> +UFS_REG_UECN = offsetof(UfsReg, uecn), >> +UFS_REG_UECT = offsetof(UfsReg, uect), >> +UFS_REG_UECDME = offsetof(UfsReg, uecdme), >> +UFS_REG_UTRIACR = offsetof(UfsReg, utriacr), >> +UFS_REG_UTRLBA = offsetof(UfsReg, utrlba), >> +UFS_REG_UTRLBAU = offsetof(UfsReg, utrlbau), >> +UFS_REG_UTRLDBR = offsetof(UfsReg, utrldbr), >> +UFS_REG_UTRLCLR = offsetof(UfsReg, utrlclr), >> +UFS_REG_UTRLRSR = offsetof(UfsReg, utrlrsr), >> +UFS_REG_UTRLCNR = offsetof(UfsReg, utrlcnr), >> +UFS_REG_UTMRLBA = offsetof(UfsReg, utmrlba), >> +UFS_REG_UTMRLBAU = offsetof(UfsReg, utmrlbau), >> +UFS_REG_UTMRLDBR = offsetof(UfsReg, utmrldbr), >> +UFS_REG_UTMRLCLR = offsetof(UfsReg, utmrlclr), >> +UFS_REG_UTMRLRSR = offsetof(UfsReg, utmrlrsr), >> +UFS_REG_UICCMD = offsetof(UfsReg, uiccmd), >> +UFS_REG_UCMDARG1 = offsetof(UfsReg, ucmdarg1), >> +UFS_REG_UCMDARG2 = offsetof(UfsReg, ucmdarg2), >> +UFS_REG_UCMDARG3 = offsetof(UfsReg, u
Re: [PATCH 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
On 26/05/2023 15:37, Thomas Huth wrote: >On 26/05/2023 07.05, Jeuk Kim wrote: >> Universal Flash Storage (UFS) is a high-performance mass storage device >> with a serial interface. It is primarily used as a high-performance >> data storage device for embedded applications. >> >> This commit contains code for UFS device to be recognized >> as a UFS PCI device. >> Patches to handle UFS logical unit and Transfer Request will follow. >> >> Signed-off-by: Jeuk Kim >> --- >> MAINTAINERS |6 + >> hw/Kconfig |1 + >> hw/meson.build |1 + >> hw/ufs/Kconfig |4 + >> hw/ufs/meson.build |1 + >> hw/ufs/trace-events | 33 + >> hw/ufs/trace.h |1 + >> hw/ufs/ufs.c | 305 ++ >> hw/ufs/ufs.h | 42 ++ >> include/block/ufs.h | 1251 ++ >> include/hw/pci/pci.h |1 + >> include/hw/pci/pci_ids.h |1 + >> meson.build |1 + > >Do you expect lots of additional files to be added to the hw/ufs/ folder? If >the answer is no, then it's maybe a little bit overkill to introduce a >separate folder for this. Wouldn't hw/block/ be a good fit for this as well? >Or maybe we could introduce hw/flash/ or so and also move the contents of >hw/nvme there? Yes. I plan to add more files to UFS for different functions (UICCMD, MQ, zoned, etc.) like nvme. So personally, I think it would be good to keep the hw/ufs/ directory.
[PATCH 2/3] hw/ufs: Support for Query Transfer Requests
This commit makes the UFS device support query and nop out transfer requests. The next patch would be support for UFS logical unit and scsi command transfer request. Signed-off-by: Jeuk Kim --- hw/ufs/ufs.c | 967 ++- hw/ufs/ufs.h | 45 +++ 2 files changed, 1011 insertions(+), 1 deletion(-) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 122ce05411..125fd179f8 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -19,6 +19,233 @@ #define UFS_MAX_NUTRS 32 #define UFS_MAX_NUTMRS 8 +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; + +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!UFS_CAP_64AS(cap) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ +uint32_t cap = ldl_le_p(>reg.cap); +hwaddr hi = addr + size - 1; +if (hi < addr) { +return MEMTX_DECODE_ERROR; +} + +if (!UFS_CAP_64AS(cap) && (hi >> 32)) { +return MEMTX_DECODE_ERROR; +} + +return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ +uint32_t utrlba = ldl_le_p(>reg.utrlba); +uint32_t utrlbau = ldl_le_p(>reg.utrlbau); +hwaddr utrl_base_addr = (((hwaddr)utrlbau) << 32) + utrlba; +hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + +return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +uint32_t cmd_desc_base_addr_lo = +le32_to_cpu(utrd->command_desc_base_addr_lo); +uint32_t cmd_desc_base_addr_hi = +le32_to_cpu(utrd->command_desc_base_addr_hi); + +return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); +uint32_t rsp_upiu_byte_off = +le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); +return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); +MemTxResult ret; + +ret = ufs_addr_read(u, utrd_addr, >utrd, sizeof(req->utrd)); +if (ret) { +trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ +UfsHc *u = req->hc; +hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +UtpUpiuReq *req_upiu = >req_upiu; +uint32_t copy_size; +uint16_t data_segment_length; +MemTxResult ret; + +/* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ +ret = ufs_addr_read(u, req_upiu_base_addr, _upiu->header, +sizeof(UtpUpiuHeader)); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +return ret; +} +data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + +copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + +data_segment_length; + +ret = ufs_addr_read(u, req_upiu_base_addr, >req_upiu, copy_size); +if (ret) { +trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); +} +return ret; +} + +static MemTxResult ufs_dma_read_prdt(UfsRequest *req) +{ +UfsHc *u = req->hc; +uint16_t prdt_len = le16_to_cpu(req->utrd.prd_table_length); +uint16_t prdt_byte_off = +le16_to_cpu(req->utrd.prd_table_offset) * sizeof(uint32_t); +uint32_t prdt_size = prdt_len * sizeof(UfshcdSgEntry); +UfshcdSgEntry *prd_entries; +hwaddr req_upiu_base_addr, prdt_base_addr; +int err; + +assert(!req->sg); + +if (prdt_len == 0) { +return MEMTX_OK; +} + +prd_entries = g_new(UfshcdSgEntry, prdt_size); +if (!prd_entries) { +trace_ufs_err_memory_allocation(); +return MEMTX_ERROR; +} + +req_upiu_base_addr = ufs_get_req_upiu_base_addr(>utrd); +prdt_base_addr = req_upiu_base_addr + prdt_byte_off; + +err = ufs_addr_read(u, prdt_base_addr, prd_entries, prdt_size); +if (err) { +trace_ufs_err_dma_read_prdt(req->slot, prdt_base_addr); +return err; +} + +req->sg = g_malloc0(sizeof(QEMUSGList)); +if (!req->sg) { +trace_ufs_err_memory_allocation(); +g_free(prd_entries); +re
[PATCH 0/3] hw/ufs: Add Universal Flash Storage (UFS) support
This patch series adds support for a new PCI-based UFS device. The UFS pci device id (PCI_DEVICE_ID_REDHAT_UFS) is not registered in the Linux kernel yet, so it does not work right away, but I confirmed that it works with Linux when the UFS pci device id is registered. I have also verified that it works with Windows 10. Jeuk Kim (3): hw/ufs: Initial commit for emulated Universal-Flash-Storage hw/ufs: Support for Query Transfer Requests hw/ufs: Support for UFS logical unit MAINTAINERS |6 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/lu.c | 1441 hw/ufs/meson.build |1 + hw/ufs/trace-events | 58 ++ hw/ufs/trace.h |1 + hw/ufs/ufs.c | 1511 ++ hw/ufs/ufs.h | 130 include/block/ufs.h | 1251 +++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + include/scsi/constants.h |1 + meson.build |1 + 15 files changed, 4409 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/lu.c create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h -- 2.34.1
[PATCH 3/3] hw/ufs: Support for UFS logical unit
This commit adds support for ufs logical unit. The LU handles processing for the SCSI command, unit descriptor query request. This commit enables the UFS device to process IO requests. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1441 ++ hw/ufs/meson.build |2 +- hw/ufs/trace-events | 25 + hw/ufs/ufs.c | 251 ++- hw/ufs/ufs.h | 43 ++ include/scsi/constants.h |1 + 6 files changed, 1757 insertions(+), 6 deletions(-) create mode 100644 hw/ufs/lu.c diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 00..ef69de61a5 --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,1441 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +/* + * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, + * with minor adjustments to make it work for UFS. + */ + +#define SCSI_DMA_BUF_SIZE (128 * KiB) +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_INQUIRY_DATA_SIZE 36 +#define SCSI_MAX_MODE_LEN 256 + +typedef struct UfsSCSIReq { +SCSIRequest req; +/* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ +uint64_t sector; +uint32_t sector_count; +uint32_t buflen; +bool started; +bool need_fua_emulation; +struct iovec iov; +QEMUIOVector qiov; +BlockAcctCookie acct; +} UfsSCSIReq; + +static void ufs_scsi_free_request(SCSIRequest *req) +{ +UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + +qemu_vfree(r->iov.iov_base); +} + +static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +{ +trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); +scsi_req_build_sense(>req, sense); +scsi_req_complete(>req, CHECK_CONDITION); +} + +static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ +UfsHc *u = UFS(req->bus->qbus.parent); +UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); +uint8_t page_code = req->cmd.buf[2]; +int start, buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +outbuf[buflen++] = lu->qdev.type & 0x1f; +outbuf[buflen++] = page_code; +outbuf[buflen++] = 0x00; +outbuf[buflen++] = 0x00; +start = buflen; + +switch (page_code) { +case 0x00: /* Supported page codes, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); +outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ +if (u->params.serial) { +outbuf[buflen++] = 0x80; /* unit serial number */ +} +outbuf[buflen++] = 0x87; /* mode page policy */ +break; +} +case 0x80: /* Device serial number, optional */ +{ +int l; + +if (!u->params.serial) { +trace_ufs_scsi_emulate_vpd_page_80_not_supported(); +return -1; +} + +l = strlen(u->params.serial); +if (l > SCSI_INQUIRY_DATA_SIZE) { +l = SCSI_INQUIRY_DATA_SIZE; +} + +trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); +memcpy(outbuf + buflen, u->params.serial, l); +buflen += l; +break; +} +case 0x87: /* Mode Page Policy, mandatory */ +{ +trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); +outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ +outbuf[buflen++] = 0xff; +outbuf[buflen++] = 0; /* shared */ +outbuf[buflen++] = 0; +break; +} +default: +return -1; +} +/* done with EVPD */ +assert(buflen - start <= 255); +outbuf[start - 1] = buflen - start; +return buflen; +} + +static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +uint32_t outbuf_len) +{ +int buflen = 0; + +if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { +return -1; +} + +if (req->cmd.buf[1] & 0x1) { +/* Vital product data */ +return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); +} + +/* Standard INQUIRY data */ +if (req->cmd.buf[2] != 0) { +return -1; +} + +/* PAGE CODE == 0 */ +buflen = req->cmd.xfer; +if (buflen > SCSI_MAX_INQUIRY_LEN) { +buflen = SCSI_MAX_INQUIRY_LEN; +} + +if (is_wlun(req
[PATCH 1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage
Universal Flash Storage (UFS) is a high-performance mass storage device with a serial interface. It is primarily used as a high-performance data storage device for embedded applications. This commit contains code for UFS device to be recognized as a UFS PCI device. Patches to handle UFS logical unit and Transfer Request will follow. Signed-off-by: Jeuk Kim --- MAINTAINERS |6 + hw/Kconfig |1 + hw/meson.build |1 + hw/ufs/Kconfig |4 + hw/ufs/meson.build |1 + hw/ufs/trace-events | 33 + hw/ufs/trace.h |1 + hw/ufs/ufs.c | 305 ++ hw/ufs/ufs.h | 42 ++ include/block/ufs.h | 1251 ++ include/hw/pci/pci.h |1 + include/hw/pci/pci_ids.h |1 + meson.build |1 + 13 files changed, 1648 insertions(+) create mode 100644 hw/ufs/Kconfig create mode 100644 hw/ufs/meson.build create mode 100644 hw/ufs/trace-events create mode 100644 hw/ufs/trace.h create mode 100644 hw/ufs/ufs.c create mode 100644 hw/ufs/ufs.h create mode 100644 include/block/ufs.h diff --git a/MAINTAINERS b/MAINTAINERS index 1c93ab0ee5..b80ba5e431 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2229,6 +2229,12 @@ F: tests/qtest/nvme-test.c F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h + megasas M: Hannes Reinecke L: qemu-block@nongnu.org diff --git a/hw/Kconfig b/hw/Kconfig index ba62ff6417..9ca7b38c31 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -38,6 +38,7 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..f01fac4617 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,6 +37,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 00..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI +bool +default y if PCI_DEVICES +depends on PCI diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 00..c1d90eeea6 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 00..17793929b1 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,33 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_memory_allocation(void) "failed to allocate memory" +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" +ufs_err_unsupport_register_offset(uint32_t offset)