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_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""
|
||||||
|
202
hw/ufs/ufs.c
202
hw/ufs/ufs.c
@ -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),
|
||||||
|
36
hw/ufs/ufs.h
36
hw/ufs/ufs.h
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user