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 <jeuk20.kim@samsung.com>
This commit is contained in:
Jeuk Kim 2023-10-19 23:28:06 +09:00
parent c60be6e3e3
commit 096434fea1
6 changed files with 334 additions and 1479 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,31 +12,6 @@ ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", l
ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", 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_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""
# lu.c
ufs_scsi_check_condition(uint32_t tag, uint8_t key, uint8_t asc, uint8_t ascq) "Command complete tag=0x%x sense=%d/%d/%d"
ufs_scsi_read_complete(uint32_t tag, size_t size) "Data ready tag=0x%x len=%zd"
ufs_scsi_read_data_count(uint32_t sector_count) "Read sector_count=%d"
ufs_scsi_read_data_invalid(void) "Data transfer direction invalid"
ufs_scsi_write_complete_noio(uint32_t tag, size_t size) "Write complete tag=0x%x more=%zd"
ufs_scsi_write_data_invalid(void) "Data transfer direction invalid"
ufs_scsi_emulate_vpd_page_00(size_t xfer) "Inquiry EVPD[Supported pages] buffer size %zd"
ufs_scsi_emulate_vpd_page_80_not_supported(void) "Inquiry EVPD[Serial number] not supported"
ufs_scsi_emulate_vpd_page_80(size_t xfer) "Inquiry EVPD[Serial number] buffer size %zd"
ufs_scsi_emulate_vpd_page_87(size_t xfer) "Inquiry EVPD[Mode Page Policy] buffer size %zd"
ufs_scsi_emulate_mode_sense(int cmd, int page, size_t xfer, int control) "Mode Sense(%d) (page %d, xfer %zd, page_control %d)"
ufs_scsi_emulate_read_data(int buflen) "Read buf_len=%d"
ufs_scsi_emulate_write_data(int buflen) "Write buf_len=%d"
ufs_scsi_emulate_command_START_STOP(void) "START STOP UNIT"
ufs_scsi_emulate_command_FORMAT_UNIT(void) "FORMAT UNIT"
ufs_scsi_emulate_command_SEND_DIAGNOSTIC(void) "SEND DIAGNOSTIC"
ufs_scsi_emulate_command_SAI_16(void) "SAI READ CAPACITY(16)"
ufs_scsi_emulate_command_SAI_unsupported(void) "Unsupported Service Action In"
ufs_scsi_emulate_command_MODE_SELECT_10(size_t xfer) "Mode Select(10) (len %zd)"
ufs_scsi_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)"
ufs_scsi_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)"
ufs_scsi_dma_command_READ(uint64_t lba, uint32_t len) "Read (block %" PRIu64 ", count %u)"
ufs_scsi_dma_command_WRITE(uint64_t lba, int len) "Write (block %" PRIu64 ", count %u)"
# error condition # 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_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_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""

View File

@ -24,6 +24,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "migration/vmstate.h" #include "migration/vmstate.h"
#include "scsi/constants.h"
#include "trace.h" #include "trace.h"
#include "ufs.h" #include "ufs.h"
@ -62,8 +63,6 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf,
return pci_dma_write(PCI_DEVICE(u), addr, buf, size); 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) 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 utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba;
@ -163,11 +162,13 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req)
req->sg = g_malloc0(sizeof(QEMUSGList)); req->sg = g_malloc0(sizeof(QEMUSGList));
pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len); pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
req->data_len = 0;
for (uint16_t i = 0; i < prdt_len; ++i) { for (uint16_t i = 0; i < prdt_len; ++i) {
hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr); hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr);
uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1; uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1;
qemu_sglist_add(req->sg, data_dma_addr, data_byte_count); qemu_sglist_add(req->sg, data_dma_addr, data_byte_count);
req->data_len += data_byte_count;
} }
return MEMTX_OK; return MEMTX_OK;
} }
@ -433,23 +434,10 @@ static const MemoryRegionOps ufs_mmio_ops = {
}, },
}; };
static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req)
{
UfsRequest *req = scsi_req->hba_private;
return req->sg;
}
static void ufs_build_upiu_sense_data(UfsRequest *req, SCSIRequest *scsi_req) void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
{ uint8_t response, uint8_t scsi_status,
req->rsp_upiu.sr.sense_data_len = cpu_to_be16(scsi_req->sense_len); uint16_t data_segment_length)
assert(scsi_req->sense_len <= SCSI_SENSE_LEN);
memcpy(req->rsp_upiu.sr.sense_data, scsi_req->sense, scsi_req->sense_len);
}
static 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)
{ {
memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader)); memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader));
req->rsp_upiu.header.trans_type = trans_type; req->rsp_upiu.header.trans_type = trans_type;
@ -459,96 +447,38 @@ static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type,
req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length); req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
} }
static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
{
UfsRequest *req = scsi_req->hba_private;
int16_t status = scsi_req->status;
uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len);
uint32_t transfered_len = scsi_req->cmd.xfer - resid;
uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCESS;
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;
}
if (status != 0) {
ufs_build_upiu_sense_data(req, scsi_req);
response = UFS_COMMAND_RESULT_FAIL;
}
data_segment_length = cpu_to_be16(scsi_req->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);
ufs_complete_req(req, UFS_REQUEST_SUCCESS);
scsi_req->hba_private = NULL;
scsi_req_unref(scsi_req);
}
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 UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req) static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req)
{ {
UfsHc *u = req->hc; UfsHc *u = req->hc;
uint8_t lun = req->req_upiu.header.lun; uint8_t lun = req->req_upiu.header.lun;
uint8_t task_tag = req->req_upiu.header.task_tag;
SCSIDevice *dev = NULL; UfsLu *lu = NULL;
trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]); trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]);
if (!is_wlun(lun)) { if (!is_wlun(lun) && (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) {
if (lun >= u->device_desc.number_lu) { trace_ufs_err_scsi_cmd_invalid_lun(lun);
trace_ufs_err_scsi_cmd_invalid_lun(lun); return UFS_REQUEST_FAIL;
return UFS_REQUEST_FAIL;
} else if (u->lus[lun] == NULL) {
trace_ufs_err_scsi_cmd_invalid_lun(lun);
return UFS_REQUEST_FAIL;
}
} }
switch (lun) { switch (lun) {
case UFS_UPIU_REPORT_LUNS_WLUN: case UFS_UPIU_REPORT_LUNS_WLUN:
dev = &u->report_wlu->qdev; lu = &u->report_wlu;
break; break;
case UFS_UPIU_UFS_DEVICE_WLUN: case UFS_UPIU_UFS_DEVICE_WLUN:
dev = &u->dev_wlu->qdev; lu = &u->dev_wlu;
break; break;
case UFS_UPIU_BOOT_WLUN: case UFS_UPIU_BOOT_WLUN:
dev = &u->boot_wlu->qdev; lu = &u->boot_wlu;
break; break;
case UFS_UPIU_RPMB_WLUN: case UFS_UPIU_RPMB_WLUN:
dev = &u->rpmb_wlu->qdev; lu = &u->rpmb_wlu;
break; break;
default: default:
dev = &u->lus[lun]->qdev; lu = u->lus[lun];
} }
SCSIRequest *scsi_req = scsi_req_new( return lu->scsi_op(lu, req);
dev, task_tag, lun, req->req_upiu.sc.cdb, UFS_CDB_SIZE, req);
uint32_t len = scsi_req_enqueue(scsi_req);
if (len) {
scsi_req_continue(scsi_req);
}
return UFS_REQUEST_NO_COMPLETE;
} }
static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req) static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req)
@ -1137,7 +1067,7 @@ static void ufs_process_req(void *opaque)
} }
} }
static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
{ {
UfsHc *u = req->hc; UfsHc *u = req->hc;
assert(req->state == UFS_REQUEST_RUNNING); assert(req->state == UFS_REQUEST_RUNNING);
@ -1159,6 +1089,7 @@ static void ufs_clear_req(UfsRequest *req)
qemu_sglist_destroy(req->sg); qemu_sglist_destroy(req->sg);
g_free(req->sg); g_free(req->sg);
req->sg = NULL; req->sg = NULL;
req->data_len = 0;
} }
memset(&req->utrd, 0, sizeof(req->utrd)); memset(&req->utrd, 0, sizeof(req->utrd));
@ -1317,28 +1248,6 @@ static void ufs_init_hc(UfsHc *u)
u->flags.permanently_disable_fw_update = 1; u->flags.permanently_disable_fw_update = 1;
} }
static bool ufs_init_wlu(UfsHc *u, UfsWLu **wlu, uint8_t wlun, Error **errp)
{
UfsWLu *new_wlu = UFSWLU(qdev_new(TYPE_UFS_WLU));
qdev_prop_set_uint32(DEVICE(new_wlu), "lun", wlun);
/*
* The well-known lu shares the same bus as the normal lu. If the well-known
* lu writes the same channel value as the normal lu, the report will be
* made not only for the normal lu but also for the well-known lu at
* REPORT_LUN time. To prevent this, the channel value of normal lu is fixed
* to 0 and the channel value of well-known lu is fixed to 1.
*/
qdev_prop_set_uint32(DEVICE(new_wlu), "channel", 1);
if (!qdev_realize_and_unref(DEVICE(new_wlu), BUS(&u->bus), errp)) {
return false;
}
*wlu = new_wlu;
return true;
}
static void ufs_realize(PCIDevice *pci_dev, Error **errp) static void ufs_realize(PCIDevice *pci_dev, Error **errp)
{ {
UfsHc *u = UFS(pci_dev); UfsHc *u = UFS(pci_dev);
@ -1349,53 +1258,21 @@ static void ufs_realize(PCIDevice *pci_dev, Error **errp)
qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev,
u->parent_obj.qdev.id); u->parent_obj.qdev.id);
u->bus.parent_bus.info = &ufs_scsi_info;
ufs_init_state(u); ufs_init_state(u);
ufs_init_hc(u); ufs_init_hc(u);
ufs_init_pci(u, pci_dev); ufs_init_pci(u, pci_dev);
if (!ufs_init_wlu(u, &u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN, errp)) { ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN);
return; ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN);
} ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN);
ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN);
if (!ufs_init_wlu(u, &u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN, errp)) {
return;
}
if (!ufs_init_wlu(u, &u->boot_wlu, UFS_UPIU_BOOT_WLUN, errp)) {
return;
}
if (!ufs_init_wlu(u, &u->rpmb_wlu, UFS_UPIU_RPMB_WLUN, errp)) {
return;
}
} }
static void ufs_exit(PCIDevice *pci_dev) static void ufs_exit(PCIDevice *pci_dev)
{ {
UfsHc *u = UFS(pci_dev); UfsHc *u = UFS(pci_dev);
if (u->dev_wlu) {
object_unref(OBJECT(u->dev_wlu));
u->dev_wlu = NULL;
}
if (u->report_wlu) {
object_unref(OBJECT(u->report_wlu));
u->report_wlu = NULL;
}
if (u->rpmb_wlu) {
object_unref(OBJECT(u->rpmb_wlu));
u->rpmb_wlu = NULL;
}
if (u->boot_wlu) {
object_unref(OBJECT(u->boot_wlu));
u->boot_wlu = NULL;
}
qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->doorbell_bh);
qemu_bh_delete(u->complete_bh); qemu_bh_delete(u->complete_bh);
@ -1437,43 +1314,18 @@ static void ufs_class_init(ObjectClass *oc, void *data)
static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev,
Error **errp) Error **errp)
{ {
SCSIDevice *dev = SCSI_DEVICE(qdev); if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) != 0) {
UfsBusClass *ubc = UFS_BUS_GET_CLASS(qbus);
UfsHc *u = UFS(qbus->parent);
if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_WLU) == 0) {
if (dev->lun != UFS_UPIU_REPORT_LUNS_WLUN &&
dev->lun != UFS_UPIU_UFS_DEVICE_WLUN &&
dev->lun != UFS_UPIU_BOOT_WLUN && dev->lun != UFS_UPIU_RPMB_WLUN) {
error_setg(errp, "bad well-known lun: %d", dev->lun);
return false;
}
if ((dev->lun == UFS_UPIU_REPORT_LUNS_WLUN && u->report_wlu != NULL) ||
(dev->lun == UFS_UPIU_UFS_DEVICE_WLUN && u->dev_wlu != NULL) ||
(dev->lun == UFS_UPIU_BOOT_WLUN && u->boot_wlu != NULL) ||
(dev->lun == UFS_UPIU_RPMB_WLUN && u->rpmb_wlu != NULL)) {
error_setg(errp, "well-known lun %d already exists", dev->lun);
return false;
}
return true;
}
if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_LU) != 0) {
error_setg(errp, "%s cannot be connected to ufs-bus", error_setg(errp, "%s cannot be connected to ufs-bus",
object_get_typename(OBJECT(dev))); object_get_typename(OBJECT(qdev)));
return false; return false;
} }
return ubc->parent_check_address(qbus, qdev, errp); return true;
} }
static void ufs_bus_class_init(ObjectClass *class, void *data) static void ufs_bus_class_init(ObjectClass *class, void *data)
{ {
BusClass *bc = BUS_CLASS(class); BusClass *bc = BUS_CLASS(class);
UfsBusClass *ubc = UFS_BUS_CLASS(class);
ubc->parent_check_address = bc->check_address;
bc->check_address = ufs_bus_check_address; bc->check_address = ufs_bus_check_address;
} }
@ -1487,7 +1339,7 @@ static const TypeInfo ufs_info = {
static const TypeInfo ufs_bus_info = { static const TypeInfo ufs_bus_info = {
.name = TYPE_UFS_BUS, .name = TYPE_UFS_BUS,
.parent = TYPE_SCSI_BUS, .parent = TYPE_BUS,
.class_init = ufs_bus_class_init, .class_init = ufs_bus_class_init,
.class_size = sizeof(UfsBusClass), .class_size = sizeof(UfsBusClass),
.instance_size = sizeof(UfsBus), .instance_size = sizeof(UfsBus),

View File

@ -16,7 +16,8 @@
#include "block/ufs.h" #include "block/ufs.h"
#define UFS_MAX_LUS 32 #define UFS_MAX_LUS 32
#define UFS_BLOCK_SIZE 4096 #define UFS_BLOCK_SIZE_SHIFT 12
#define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT)
typedef struct UfsBusClass { typedef struct UfsBusClass {
BusClass parent_class; BusClass parent_class;
@ -24,7 +25,7 @@ typedef struct UfsBusClass {
} UfsBusClass; } UfsBusClass;
typedef struct UfsBus { typedef struct UfsBus {
SCSIBus parent_bus; BusState parent_bus;
} UfsBus; } UfsBus;
#define TYPE_UFS_BUS "ufs-bus" #define TYPE_UFS_BUS "ufs-bus"
@ -55,19 +56,22 @@ typedef struct UfsRequest {
/* for scsi command */ /* for scsi command */
QEMUSGList *sg; QEMUSGList *sg;
uint32_t data_len;
} UfsRequest; } UfsRequest;
struct UfsLu;
typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *);
typedef struct UfsLu { typedef struct UfsLu {
SCSIDevice qdev; DeviceState qdev;
uint8_t lun; uint8_t lun;
UnitDescriptor unit_desc; UnitDescriptor unit_desc;
SCSIBus bus;
SCSIDevice *scsi_dev;
BlockConf conf;
UfsScsiOp scsi_op;
} UfsLu; } UfsLu;
typedef struct UfsWLu {
SCSIDevice qdev;
uint8_t lun;
} UfsWLu;
typedef struct UfsParams { typedef struct UfsParams {
char *serial; char *serial;
uint8_t nutrs; /* Number of UTP Transfer Request Slots */ uint8_t nutrs; /* Number of UTP Transfer Request Slots */
@ -84,10 +88,10 @@ typedef struct UfsHc {
UfsRequest *req_list; UfsRequest *req_list;
UfsLu *lus[UFS_MAX_LUS]; UfsLu *lus[UFS_MAX_LUS];
UfsWLu *report_wlu; UfsLu report_wlu;
UfsWLu *dev_wlu; UfsLu dev_wlu;
UfsWLu *boot_wlu; UfsLu boot_wlu;
UfsWLu *rpmb_wlu; UfsLu rpmb_wlu;
DeviceDescriptor device_desc; DeviceDescriptor device_desc;
GeometryDescriptor geometry_desc; GeometryDescriptor geometry_desc;
Attributes attributes; Attributes attributes;
@ -104,9 +108,6 @@ typedef struct UfsHc {
#define TYPE_UFS_LU "ufs-lu" #define TYPE_UFS_LU "ufs-lu"
#define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU) #define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU)
#define TYPE_UFS_WLU "ufs-wlu"
#define UFSWLU(obj) OBJECT_CHECK(UfsWLu, (obj), TYPE_UFS_WLU)
typedef enum UfsQueryFlagPerm { typedef enum UfsQueryFlagPerm {
UFS_QUERY_FLAG_NONE = 0x0, UFS_QUERY_FLAG_NONE = 0x0,
UFS_QUERY_FLAG_READ = 0x1, UFS_QUERY_FLAG_READ = 0x1,
@ -128,4 +129,9 @@ static inline bool is_wlun(uint8_t lun)
lun == UFS_UPIU_RPMB_WLUN); lun == UFS_UPIU_RPMB_WLUN);
} }
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);
void ufs_complete_req(UfsRequest *req, UfsReqResult req_result);
void ufs_init_wlu(UfsLu *wlu, uint8_t wlun);
#endif /* HW_UFS_UFS_H */ #endif /* HW_UFS_UFS_H */

View File

@ -379,7 +379,7 @@ typedef struct Attributes {
/* Command response result code */ /* Command response result code */
typedef enum CommandRespCode { typedef enum CommandRespCode {
UFS_COMMAND_RESULT_SUCESS = 0x00, UFS_COMMAND_RESULT_SUCCESS = 0x00,
UFS_COMMAND_RESULT_FAIL = 0x01, UFS_COMMAND_RESULT_FAIL = 0x01,
} CommandRespCode; } CommandRespCode;

View File

@ -425,6 +425,9 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = { const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = {
TEST_UNIT_READY, TEST_UNIT_READY,
}; };
const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
REQUEST_SENSE,
};
UtpTransferReqDesc utrd; UtpTransferReqDesc utrd;
UtpUpiuRsp rsp_upiu; UtpUpiuRsp rsp_upiu;
@ -440,6 +443,12 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
/* There is one logical unit whose lun is 0 */ /* There is one logical unit whose lun is 0 */
g_assert_cmpuint(buf[9], ==, 0); g_assert_cmpuint(buf[9], ==, 0);
/* Clear Unit Attention */
ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf,
sizeof(buf), &utrd, &rsp_upiu);
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION);
/* Check TEST_UNIT_READY */ /* Check TEST_UNIT_READY */
ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
&utrd, &rsp_upiu); &utrd, &rsp_upiu);
@ -473,6 +482,9 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
0x00, 0x00,
0x00 0x00
}; };
const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
REQUEST_SENSE,
};
const uint8_t read_cdb[UFS_CDB_SIZE] = { const uint8_t read_cdb[UFS_CDB_SIZE] = {
/* READ(10) to LBA 0, transfer length 1 */ /* READ(10) to LBA 0, transfer length 1 */
READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
@ -484,32 +496,39 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
uint32_t block_size; uint32_t block_size;
UtpTransferReqDesc utrd; UtpTransferReqDesc utrd;
UtpUpiuRsp rsp_upiu; UtpUpiuRsp rsp_upiu;
const int test_lun = 1;
ufs_init(ufs, alloc); ufs_init(ufs, alloc);
/* Clear Unit Attention */
ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0,
read_buf, sizeof(read_buf), &utrd, &rsp_upiu);
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION);
/* Read capacity */ /* Read capacity */
ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf, ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0,
sizeof(read_buf), &utrd, &rsp_upiu); read_buf, sizeof(read_buf), &utrd, &rsp_upiu);
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
UFS_COMMAND_RESULT_SUCESS); UFS_COMMAND_RESULT_SUCCESS);
block_size = ldl_be_p(&read_buf[8]); block_size = ldl_be_p(&read_buf[8]);
g_assert_cmpuint(block_size, ==, 4096); g_assert_cmpuint(block_size, ==, 4096);
/* Write data */ /* Write data */
memset(write_buf, 0xab, block_size); memset(write_buf, 0xab, block_size);
ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size,
&utrd, &rsp_upiu); NULL, 0, &utrd, &rsp_upiu);
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
UFS_COMMAND_RESULT_SUCESS); UFS_COMMAND_RESULT_SUCCESS);
/* Read data and verify */ /* Read data and verify */
ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size, ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf,
&utrd, &rsp_upiu); block_size, &utrd, &rsp_upiu);
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
UFS_COMMAND_RESULT_SUCESS); UFS_COMMAND_RESULT_SUCCESS);
g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0);
ufs_exit(ufs, alloc); ufs_exit(ufs, alloc);