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:
parent
c60be6e3e3
commit
096434fea1
1511
hw/ufs/lu.c
1511
hw/ufs/lu.c
File diff suppressed because it is too large
Load Diff
@ -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_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
|
||||
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""
|
||||
|
202
hw/ufs/ufs.c
202
hw/ufs/ufs.c
@ -24,6 +24,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "scsi/constants.h"
|
||||
#include "trace.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);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -163,11 +162,13 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req)
|
||||
|
||||
req->sg = g_malloc0(sizeof(QEMUSGList));
|
||||
pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
|
||||
req->data_len = 0;
|
||||
|
||||
for (uint16_t i = 0; i < prdt_len; ++i) {
|
||||
hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr);
|
||||
uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1;
|
||||
qemu_sglist_add(req->sg, data_dma_addr, data_byte_count);
|
||||
req->data_len += data_byte_count;
|
||||
}
|
||||
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)
|
||||
{
|
||||
req->rsp_upiu.sr.sense_data_len = cpu_to_be16(scsi_req->sense_len);
|
||||
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)
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
UfsHc *u = req->hc;
|
||||
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]);
|
||||
|
||||
if (!is_wlun(lun)) {
|
||||
if (lun >= u->device_desc.number_lu) {
|
||||
trace_ufs_err_scsi_cmd_invalid_lun(lun);
|
||||
return UFS_REQUEST_FAIL;
|
||||
} else if (u->lus[lun] == NULL) {
|
||||
trace_ufs_err_scsi_cmd_invalid_lun(lun);
|
||||
return UFS_REQUEST_FAIL;
|
||||
}
|
||||
if (!is_wlun(lun) && (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) {
|
||||
trace_ufs_err_scsi_cmd_invalid_lun(lun);
|
||||
return UFS_REQUEST_FAIL;
|
||||
}
|
||||
|
||||
switch (lun) {
|
||||
case UFS_UPIU_REPORT_LUNS_WLUN:
|
||||
dev = &u->report_wlu->qdev;
|
||||
lu = &u->report_wlu;
|
||||
break;
|
||||
case UFS_UPIU_UFS_DEVICE_WLUN:
|
||||
dev = &u->dev_wlu->qdev;
|
||||
lu = &u->dev_wlu;
|
||||
break;
|
||||
case UFS_UPIU_BOOT_WLUN:
|
||||
dev = &u->boot_wlu->qdev;
|
||||
lu = &u->boot_wlu;
|
||||
break;
|
||||
case UFS_UPIU_RPMB_WLUN:
|
||||
dev = &u->rpmb_wlu->qdev;
|
||||
lu = &u->rpmb_wlu;
|
||||
break;
|
||||
default:
|
||||
dev = &u->lus[lun]->qdev;
|
||||
lu = u->lus[lun];
|
||||
}
|
||||
|
||||
SCSIRequest *scsi_req = scsi_req_new(
|
||||
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;
|
||||
return lu->scsi_op(lu, 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;
|
||||
assert(req->state == UFS_REQUEST_RUNNING);
|
||||
@ -1159,6 +1089,7 @@ static void ufs_clear_req(UfsRequest *req)
|
||||
qemu_sglist_destroy(req->sg);
|
||||
g_free(req->sg);
|
||||
req->sg = NULL;
|
||||
req->data_len = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
u->parent_obj.qdev.id);
|
||||
u->bus.parent_bus.info = &ufs_scsi_info;
|
||||
|
||||
ufs_init_state(u);
|
||||
ufs_init_hc(u);
|
||||
ufs_init_pci(u, pci_dev);
|
||||
|
||||
if (!ufs_init_wlu(u, &u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN);
|
||||
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);
|
||||
}
|
||||
|
||||
static void ufs_exit(PCIDevice *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->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,
|
||||
Error **errp)
|
||||
{
|
||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||
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) {
|
||||
if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) != 0) {
|
||||
error_setg(errp, "%s cannot be connected to ufs-bus",
|
||||
object_get_typename(OBJECT(dev)));
|
||||
object_get_typename(OBJECT(qdev)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return ubc->parent_check_address(qbus, qdev, errp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ufs_bus_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1487,7 +1339,7 @@ static const TypeInfo ufs_info = {
|
||||
|
||||
static const TypeInfo ufs_bus_info = {
|
||||
.name = TYPE_UFS_BUS,
|
||||
.parent = TYPE_SCSI_BUS,
|
||||
.parent = TYPE_BUS,
|
||||
.class_init = ufs_bus_class_init,
|
||||
.class_size = sizeof(UfsBusClass),
|
||||
.instance_size = sizeof(UfsBus),
|
||||
|
36
hw/ufs/ufs.h
36
hw/ufs/ufs.h
@ -16,7 +16,8 @@
|
||||
#include "block/ufs.h"
|
||||
|
||||
#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 {
|
||||
BusClass parent_class;
|
||||
@ -24,7 +25,7 @@ typedef struct UfsBusClass {
|
||||
} UfsBusClass;
|
||||
|
||||
typedef struct UfsBus {
|
||||
SCSIBus parent_bus;
|
||||
BusState parent_bus;
|
||||
} UfsBus;
|
||||
|
||||
#define TYPE_UFS_BUS "ufs-bus"
|
||||
@ -55,19 +56,22 @@ typedef struct UfsRequest {
|
||||
|
||||
/* for scsi command */
|
||||
QEMUSGList *sg;
|
||||
uint32_t data_len;
|
||||
} UfsRequest;
|
||||
|
||||
struct UfsLu;
|
||||
typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *);
|
||||
|
||||
typedef struct UfsLu {
|
||||
SCSIDevice qdev;
|
||||
DeviceState qdev;
|
||||
uint8_t lun;
|
||||
UnitDescriptor unit_desc;
|
||||
SCSIBus bus;
|
||||
SCSIDevice *scsi_dev;
|
||||
BlockConf conf;
|
||||
UfsScsiOp scsi_op;
|
||||
} UfsLu;
|
||||
|
||||
typedef struct UfsWLu {
|
||||
SCSIDevice qdev;
|
||||
uint8_t lun;
|
||||
} UfsWLu;
|
||||
|
||||
typedef struct UfsParams {
|
||||
char *serial;
|
||||
uint8_t nutrs; /* Number of UTP Transfer Request Slots */
|
||||
@ -84,10 +88,10 @@ typedef struct UfsHc {
|
||||
UfsRequest *req_list;
|
||||
|
||||
UfsLu *lus[UFS_MAX_LUS];
|
||||
UfsWLu *report_wlu;
|
||||
UfsWLu *dev_wlu;
|
||||
UfsWLu *boot_wlu;
|
||||
UfsWLu *rpmb_wlu;
|
||||
UfsLu report_wlu;
|
||||
UfsLu dev_wlu;
|
||||
UfsLu boot_wlu;
|
||||
UfsLu rpmb_wlu;
|
||||
DeviceDescriptor device_desc;
|
||||
GeometryDescriptor geometry_desc;
|
||||
Attributes attributes;
|
||||
@ -104,9 +108,6 @@ typedef struct UfsHc {
|
||||
#define TYPE_UFS_LU "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 {
|
||||
UFS_QUERY_FLAG_NONE = 0x0,
|
||||
UFS_QUERY_FLAG_READ = 0x1,
|
||||
@ -128,4 +129,9 @@ static inline bool is_wlun(uint8_t lun)
|
||||
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 */
|
||||
|
@ -379,7 +379,7 @@ typedef struct Attributes {
|
||||
|
||||
/* Command response result code */
|
||||
typedef enum CommandRespCode {
|
||||
UFS_COMMAND_RESULT_SUCESS = 0x00,
|
||||
UFS_COMMAND_RESULT_SUCCESS = 0x00,
|
||||
UFS_COMMAND_RESULT_FAIL = 0x01,
|
||||
} CommandRespCode;
|
||||
|
||||
|
@ -425,6 +425,9 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
|
||||
const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = {
|
||||
TEST_UNIT_READY,
|
||||
};
|
||||
const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
|
||||
REQUEST_SENSE,
|
||||
};
|
||||
UtpTransferReqDesc utrd;
|
||||
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 */
|
||||
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 */
|
||||
ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
|
||||
&utrd, &rsp_upiu);
|
||||
@ -473,6 +482,9 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
|
||||
REQUEST_SENSE,
|
||||
};
|
||||
const uint8_t read_cdb[UFS_CDB_SIZE] = {
|
||||
/* READ(10) to LBA 0, transfer length 1 */
|
||||
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;
|
||||
UtpTransferReqDesc utrd;
|
||||
UtpUpiuRsp rsp_upiu;
|
||||
const int test_lun = 1;
|
||||
|
||||
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 */
|
||||
ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf,
|
||||
sizeof(read_buf), &utrd, &rsp_upiu);
|
||||
ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_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, ==,
|
||||
UFS_COMMAND_RESULT_SUCESS);
|
||||
UFS_COMMAND_RESULT_SUCCESS);
|
||||
block_size = ldl_be_p(&read_buf[8]);
|
||||
g_assert_cmpuint(block_size, ==, 4096);
|
||||
|
||||
/* Write data */
|
||||
memset(write_buf, 0xab, block_size);
|
||||
ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0,
|
||||
&utrd, &rsp_upiu);
|
||||
ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size,
|
||||
NULL, 0, &utrd, &rsp_upiu);
|
||||
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.header.scsi_status, ==,
|
||||
UFS_COMMAND_RESULT_SUCESS);
|
||||
UFS_COMMAND_RESULT_SUCCESS);
|
||||
|
||||
/* Read data and verify */
|
||||
ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size,
|
||||
&utrd, &rsp_upiu);
|
||||
ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf,
|
||||
block_size, &utrd, &rsp_upiu);
|
||||
g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS);
|
||||
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);
|
||||
|
||||
ufs_exit(ufs, alloc);
|
||||
|
Loading…
Reference in New Issue
Block a user