All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Hajnoczi <stefanha@redhat.com>
To: Jeuk Kim <jeuk20.kim@samsung.com>
Cc: "qemu-devel@nongnu.org" <qemu-devel@nongnu.org>,
	"kwolf@redhat.com" <kwolf@redhat.com>,
	"hreitz@redhat.com" <hreitz@redhat.com>,
	"pbonzini@redhat.com" <pbonzini@redhat.com>,
	"fam@euphon.net" <fam@euphon.net>,
	"qemu-block@nongnu.org" <qemu-block@nongnu.org>,
	Klaus Birkelund Jensen <k.jensen@samsung.com>
Subject: Re: [PATCH v2 2/3] hw/ufs: Support for Query Transfer Requests
Date: Mon, 19 Jun 2023 09:16:33 +0200	[thread overview]
Message-ID: <20230619071633.GA2497337@fedora> (raw)
In-Reply-To: <20230616065825epcms2p5682ea0edcd1fb0366a344708bea875cb@epcms2p5>

[-- Attachment #1: Type: text/plain, Size: 41080 bytes --]

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 <jeuk20.kim@samsung.com>
> ---
>  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(&u->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(&u->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(&u->reg.utrlba);
> +    uint32_t utrlbau = ldl_le_p(&u->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, &req->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(&req->utrd);
> +    UtpUpiuReq *req_upiu = &req->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, &req_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->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(&req->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;

prd_entries is leaked. I suggest using g_autofree to avoid manual
g_free() calls in return paths.

> +    }
> +
> +    req->sg = g_malloc0(sizeof(QEMUSGList));
> +    if (!req->sg) {
> +        trace_ufs_err_memory_allocation();
> +        g_free(prd_entries);
> +        return MEMTX_ERROR;
> +    }
> +    pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
> +
> +    for (uint16_t i = 0; i < prdt_len; ++i) {
> +        hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr);
> +        int32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1;
> +        qemu_sglist_add(req->sg, data_dma_addr, data_byte_count);
> +    }
> +    g_free(prd_entries);
> +
> +    return MEMTX_OK;
> +}
> +
> +static MemTxResult ufs_dma_read_upiu(UfsRequest *req)
> +{
> +    MemTxResult ret;
> +
> +    ret = ufs_dma_read_utrd(req);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    ret = ufs_dma_read_req_upiu(req);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    ret = ufs_dma_read_prdt(req);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +static MemTxResult ufs_dma_write_utrd(UfsRequest *req)
> +{
> +    UfsHc *u = req->hc;
> +    hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
> +    MemTxResult ret;
> +
> +    ret = ufs_addr_write(u, utrd_addr, &req->utrd, sizeof(req->utrd));
> +    if (ret) {
> +        trace_ufs_err_dma_write_utrd(req->slot, utrd_addr);
> +    }
> +    return ret;
> +}
> +
> +static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req)
> +{
> +    UfsHc *u = req->hc;
> +    hwaddr rsp_upiu_base_addr = ufs_get_rsp_upiu_base_addr(&req->utrd);
> +    uint32_t rsp_upiu_byte_len =
> +        le16_to_cpu(req->utrd.response_upiu_length) * sizeof(uint32_t);
> +    uint16_t data_segment_length =
> +        be16_to_cpu(req->rsp_upiu.header.data_segment_length);
> +    uint32_t copy_size = sizeof(UtpUpiuHeader) +
> +                         UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
> +                         data_segment_length;
> +    MemTxResult ret;
> +
> +    if (copy_size > rsp_upiu_byte_len) {
> +        copy_size = rsp_upiu_byte_len;
> +    }
> +
> +    ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size);
> +    if (ret) {
> +        trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr);
> +    }
> +    return ret;
> +}
> +
> +static MemTxResult ufs_dma_write_upiu(UfsRequest *req)
> +{
> +    MemTxResult ret;
> +
> +    ret = ufs_dma_write_rsp_upiu(req);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    return ufs_dma_write_utrd(req);
> +}
> +
>  static void ufs_irq_check(UfsHc *u)
>  {
>      PCIDevice *pci = PCI_DEVICE(u);
> @@ -34,6 +261,36 @@ static void ufs_irq_check(UfsHc *u)
>      }
>  }
>  
> +static void ufs_process_db(UfsHc *u, uint64_t val)
> +{
> +    uint32_t slot;
> +    uint32_t nutrs = u->params.nutrs;
> +    uint32_t utrldbr = ldl_le_p(&u->reg.utrldbr);
> +    UfsRequest *req;
> +
> +    val &= ~utrldbr;
> +    if (!val) {
> +        return;
> +    }
> +    stl_le_p(&u->reg.utrldbr, utrldbr | val);
> +
> +    slot = find_first_bit(&val, nutrs);
> +
> +    while (slot < nutrs) {
> +        req = &u->req_list[slot];
> +        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(&val, nutrs, slot + 1);
> +    }
> +
> +    qemu_bh_schedule(u->doorbell_bh);
> +}
> +
>  static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
>  {
>      uint32_t is = ldl_le_p(&u->reg.is);
> @@ -85,6 +342,7 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
>      uint32_t is = ldl_le_p(&u->reg.is);
>      uint32_t hcs = ldl_le_p(&u->reg.hcs);
>      uint32_t hce = ldl_le_p(&u->reg.hce);
> +    uint32_t utrldbr = ldl_le_p(&u->reg.utrldbr);
>      uint32_t utrlcnr = ldl_le_p(&u->reg.utrlcnr);
>      uint32_t utrlba, utmrlba;
>  
> @@ -119,7 +377,9 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
>          stl_le_p(&u->reg.utrlbau, data);
>          break;
>      case A_UTRLDBR:
> -        /* Not yet supported */
> +        ufs_process_db(u, data);
> +        utrldbr |= data;
> +        stl_le_p(&u->reg.utrldbr, utrldbr);
>          break;
>      case A_UTRLRSR:
>          stl_le_p(&u->reg.utrlrsr, data);
> @@ -199,6 +459,632 @@ static const MemoryRegionOps ufs_mmio_ops = {
>      },
>  };
>  
> +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));
> +    req->rsp_upiu.header.trans_type = trans_type;
> +    req->rsp_upiu.header.flags = flags;
> +    req->rsp_upiu.header.response = response;
> +    req->rsp_upiu.header.scsi_status = scsi_status;
> +    req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
> +}
> +
> +static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req)
> +{
> +    trace_ufs_exec_nop_cmd(req->slot);
> +    ufs_build_upiu_header(req, UPIU_TRANSACTION_NOP_IN, 0, 0, 0, 0);
> +    return UFS_REQUEST_SUCCESS;
> +}
> +
> +/*
> + * This defines the permission of flags based on their IDN. There are some
> + * things that are declared read-only, which is inconsistent with the ufs spec,
> + * because we want to return an error for features that are not yet supported.
> + */
> +static const int flag_permission[QUERY_FLAG_IDN_COUNT] = {
> +    [QUERY_FLAG_IDN_FDEVICEINIT] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET,
> +    /* Write protection is not supported */
> +    [QUERY_FLAG_IDN_PERMANENT_WPE] = UFS_QUERY_FLAG_READ,
> +    [QUERY_FLAG_IDN_PWR_ON_WPE] = UFS_QUERY_FLAG_READ,
> +    [QUERY_FLAG_IDN_BKOPS_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET |
> +                                UFS_QUERY_FLAG_CLEAR | UFS_QUERY_FLAG_TOGGLE,
> +    [QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE] =
> +        UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | UFS_QUERY_FLAG_CLEAR |
> +        UFS_QUERY_FLAG_TOGGLE,
> +    /* Purge Operation is not supported */
> +    [QUERY_FLAG_IDN_PURGE_ENABLE] = UFS_QUERY_FLAG_NONE,
> +    /* Refresh Operation is not supported */
> +    [QUERY_FLAG_IDN_REFRESH_ENABLE] = UFS_QUERY_FLAG_NONE,
> +    /* Physical Resource Removal is not supported */
> +    [QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL] = UFS_QUERY_FLAG_READ,
> +    [QUERY_FLAG_IDN_BUSY_RTC] = UFS_QUERY_FLAG_READ,
> +    [QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE] = UFS_QUERY_FLAG_READ,
> +    /* Write Booster is not supported */
> +    [QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ,
> +    [QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] = UFS_QUERY_FLAG_READ,
> +    [QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8] = UFS_QUERY_FLAG_READ,
> +};
> +
> +static inline QueryRespCode ufs_flag_check_idn_valid(uint8_t idn, int op)
> +{
> +    if (idn >= QUERY_FLAG_IDN_COUNT) {
> +        return QUERY_RESULT_INVALID_IDN;
> +    }
> +
> +    if (!(flag_permission[idn] & op)) {
> +        if (op == UFS_QUERY_FLAG_READ) {
> +            trace_ufs_err_query_flag_not_readable(idn);
> +            return QUERY_RESULT_NOT_READABLE;
> +        }
> +        trace_ufs_err_query_flag_not_writable(idn);
> +        return QUERY_RESULT_NOT_WRITEABLE;
> +    }
> +
> +    return QUERY_RESULT_SUCCESS;
> +}
> +
> +static const int attr_permission[QUERY_ATTR_IDN_COUNT] = {
> +    /* booting is not supported */
> +    [QUERY_ATTR_IDN_BOOT_LU_EN] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_POWER_MODE] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_ACTIVE_ICC_LVL] =
> +        UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_OOO_DATA_EN] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_BKOPS_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_PURGE_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_MAX_DATA_IN] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_MAX_DATA_OUT] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_DYN_CAP_NEEDED] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_REF_CLK_FREQ] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_CONF_DESC_LOCK] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_MAX_NUM_OF_RTT] =
> +        UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_EE_CONTROL] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_EE_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_SECONDS_PASSED] = UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_CNTX_CONF] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_FFU_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_PSA_STATE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_PSA_DATA_SIZE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
> +    [QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_CASE_ROUGH_TEMP] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_HIGH_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_LOW_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_THROTTLING_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_WB_FLUSH_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
> +    /* refresh operation is not supported */
> +    [QUERY_ATTR_IDN_REFRESH_STATUS] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_REFRESH_FREQ] = UFS_QUERY_ATTR_READ,
> +    [QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
> +};
> +
> +static inline QueryRespCode ufs_attr_check_idn_valid(uint8_t idn, int op)
> +{
> +    if (idn >= QUERY_ATTR_IDN_COUNT) {
> +        return QUERY_RESULT_INVALID_IDN;
> +    }
> +
> +    if (!(attr_permission[idn] & op)) {
> +        if (op == UFS_QUERY_ATTR_READ) {
> +            trace_ufs_err_query_attr_not_readable(idn);
> +            return QUERY_RESULT_NOT_READABLE;
> +        }
> +        trace_ufs_err_query_attr_not_writable(idn);
> +        return QUERY_RESULT_NOT_WRITEABLE;
> +    }
> +
> +    return QUERY_RESULT_SUCCESS;
> +}
> +
> +static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op)
> +{
> +    UfsHc *u = req->hc;
> +    uint8_t idn = req->req_upiu.qr.idn;
> +    uint32_t value;
> +    QueryRespCode ret;
> +
> +    ret = ufs_flag_check_idn_valid(idn, op);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    value = *(((uint8_t *)&u->flags) + idn);
> +    if (idn == QUERY_FLAG_IDN_FDEVICEINIT) {
> +        value = 0;
> +    } else if (op == UFS_QUERY_FLAG_READ) {
> +        value = *(((uint8_t *)&u->flags) + idn);

This value was already loaded a few lines above.

> +    } else if (op == UFS_QUERY_FLAG_SET) {
> +        value = 1;
> +    } else if (op == UFS_QUERY_FLAG_CLEAR) {
> +        value = 0;
> +    } else if (op == UFS_QUERY_FLAG_TOGGLE) {
> +        value = !value;
> +    } else {
> +        trace_ufs_err_query_invalid_opcode(op);
> +        return QUERY_RESULT_INVALID_OPCODE;
> +    }
> +
> +    *(((uint8_t *)&u->flags) + idn) = value;
> +    req->rsp_upiu.qr.value = cpu_to_be32(value);
> +    return QUERY_RESULT_SUCCESS;
> +}
> +
> +static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
> +{
> +    switch (idn) {
> +    case QUERY_ATTR_IDN_BOOT_LU_EN:
> +        return u->attributes.boot_lun_en;
> +    case QUERY_ATTR_IDN_POWER_MODE:
> +        return u->attributes.current_power_mode;
> +    case QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
> +        return u->attributes.active_icc_level;
> +    case QUERY_ATTR_IDN_OOO_DATA_EN:
> +        return u->attributes.out_of_order_data_en;
> +    case QUERY_ATTR_IDN_BKOPS_STATUS:
> +        return u->attributes.background_op_status;
> +    case QUERY_ATTR_IDN_PURGE_STATUS:
> +        return u->attributes.purge_status;
> +    case QUERY_ATTR_IDN_MAX_DATA_IN:
> +        return u->attributes.max_data_in_size;
> +    case QUERY_ATTR_IDN_MAX_DATA_OUT:
> +        return u->attributes.max_data_out_size;
> +    case QUERY_ATTR_IDN_DYN_CAP_NEEDED:
> +        return be32_to_cpu(u->attributes.dyn_cap_needed);
> +    case QUERY_ATTR_IDN_REF_CLK_FREQ:
> +        return u->attributes.ref_clk_freq;
> +    case QUERY_ATTR_IDN_CONF_DESC_LOCK:
> +        return u->attributes.config_descr_lock;
> +    case QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
> +        return u->attributes.max_num_of_rtt;
> +    case QUERY_ATTR_IDN_EE_CONTROL:
> +        return be16_to_cpu(u->attributes.exception_event_control);
> +    case QUERY_ATTR_IDN_EE_STATUS:
> +        return be16_to_cpu(u->attributes.exception_event_status);
> +    case QUERY_ATTR_IDN_SECONDS_PASSED:
> +        return be32_to_cpu(u->attributes.seconds_passed);
> +    case QUERY_ATTR_IDN_CNTX_CONF:
> +        return be16_to_cpu(u->attributes.context_conf);
> +    case QUERY_ATTR_IDN_FFU_STATUS:
> +        return u->attributes.device_ffu_status;
> +    case QUERY_ATTR_IDN_PSA_STATE:
> +        return be32_to_cpu(u->attributes.psa_state);
> +    case QUERY_ATTR_IDN_PSA_DATA_SIZE:
> +        return u->attributes.psa_data_size;
> +    case QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME:
> +        return u->attributes.ref_clk_gating_wait_time;
> +    case QUERY_ATTR_IDN_CASE_ROUGH_TEMP:
> +        return u->attributes.device_case_rough_temperaure;
> +    case QUERY_ATTR_IDN_HIGH_TEMP_BOUND:
> +        return u->attributes.device_too_high_temp_boundary;
> +    case QUERY_ATTR_IDN_LOW_TEMP_BOUND:
> +        return u->attributes.device_too_low_temp_boundary;
> +    case QUERY_ATTR_IDN_THROTTLING_STATUS:
> +        return u->attributes.throttling_status;
> +    case QUERY_ATTR_IDN_WB_FLUSH_STATUS:
> +        return u->attributes.wb_buffer_flush_status;
> +    case QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE:
> +        return u->attributes.available_wb_buffer_size;
> +    case QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST:
> +        return u->attributes.wb_buffer_life_time_est;
> +    case QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE:
> +        return be32_to_cpu(u->attributes.current_wb_buffer_size);
> +    case QUERY_ATTR_IDN_REFRESH_STATUS:
> +        return u->attributes.refresh_status;
> +    case QUERY_ATTR_IDN_REFRESH_FREQ:
> +        return u->attributes.refresh_freq;
> +    case QUERY_ATTR_IDN_REFRESH_UNIT:
> +        return u->attributes.refresh_unit;
> +    }
> +    return 0;
> +}
> +
> +static void ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
> +{
> +    switch (idn) {
> +    case QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
> +        u->attributes.active_icc_level = value;
> +        break;
> +    case QUERY_ATTR_IDN_MAX_DATA_IN:
> +        u->attributes.max_data_in_size = value;
> +        break;
> +    case QUERY_ATTR_IDN_MAX_DATA_OUT:
> +        u->attributes.max_data_out_size = value;
> +        break;
> +    case QUERY_ATTR_IDN_REF_CLK_FREQ:
> +        u->attributes.ref_clk_freq = value;
> +        break;
> +    case QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
> +        u->attributes.max_num_of_rtt = value;
> +        break;
> +    case QUERY_ATTR_IDN_EE_CONTROL:
> +        u->attributes.exception_event_control = cpu_to_be16(value);
> +        break;
> +    case QUERY_ATTR_IDN_SECONDS_PASSED:
> +        u->attributes.seconds_passed = cpu_to_be32(value);
> +        break;
> +    case QUERY_ATTR_IDN_PSA_STATE:
> +        u->attributes.psa_state = value;
> +        break;
> +    case QUERY_ATTR_IDN_PSA_DATA_SIZE:
> +        u->attributes.psa_data_size = cpu_to_be32(value);
> +        break;
> +    }
> +}
> +
> +static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op)
> +{
> +    UfsHc *u = req->hc;
> +    uint8_t idn = req->req_upiu.qr.idn;
> +    uint32_t value;
> +    QueryRespCode ret;
> +
> +    ret = ufs_attr_check_idn_valid(idn, op);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    if (op == UFS_QUERY_ATTR_READ) {
> +        value = ufs_read_attr_value(u, idn);
> +    } else {
> +        value = be32_to_cpu(req->req_upiu.qr.value);
> +        ufs_write_attr_value(u, idn, value);
> +    }
> +
> +    req->rsp_upiu.qr.value = cpu_to_be32(value);
> +    return QUERY_RESULT_SUCCESS;
> +}
> +
> +static const RpmbUnitDescriptor rpmb_unit_desc = {
> +    .length = sizeof(RpmbUnitDescriptor),
> +    .descriptor_idn = 2,
> +    .unit_index = UFS_UPIU_RPMB_WLUN,
> +    .lu_enable = 0,
> +};
> +
> +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) {
> +        trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun);
> +        return QUERY_RESULT_INVALID_INDEX;
> +    }
> +
> +    if (lun == UFS_UPIU_RPMB_WLUN) {
> +        memcpy(&req->rsp_upiu.qr.data, &rpmb_unit_desc, rpmb_unit_desc.length);
> +    } else {
> +        /* unit descriptor is not yet supported */
> +        return QUERY_RESULT_INVALID_INDEX;
> +    }
> +
> +    return QUERY_RESULT_SUCCESS;
> +}
> +
> +static const StringDescriptor manufacturer_str_desc = {
> +    .length = 0x12,
> +    .descriptor_idn = QUERY_DESC_IDN_STRING,
> +    .UC = { 'S', 'A', 'M', 'S', 'U', 'N', 'G' },

What is the endianness of these 16-bit characters? I noticed endianness
issues in several other places but will not audit the patch
exhaustively. Please review all guest-visible fields carefully and add
cpu_to_leXX()/cpu_to_beXX() where necessary.

> +};
> +
> +static const StringDescriptor product_name_str_desc = {
> +    .length = 0x22,
> +    .descriptor_idn = QUERY_DESC_IDN_STRING,
> +    .UC = { 'Q', 'E', 'M', 'U', '-', 'U', 'F', 'S' },
> +};
> +
> +static const StringDescriptor product_rev_level_str_desc = {
> +    .length = 0x0a,
> +    .descriptor_idn = QUERY_DESC_IDN_STRING,
> +    .UC = { '0', '0', '0', '1' },
> +};
> +
> +static const StringDescriptor null_str_desc = {
> +    .length = 0x02,
> +    .descriptor_idn = QUERY_DESC_IDN_STRING,
> +};
> +
> +static QueryRespCode ufs_read_string_desc(UfsRequest *req)
> +{
> +    UfsHc *u = req->hc;
> +    uint8_t index = req->req_upiu.qr.index;
> +    if (index == u->device_desc.manufacturer_name) {
> +        memcpy(&req->rsp_upiu.qr.data, &manufacturer_str_desc,
> +               manufacturer_str_desc.length);
> +    } else if (index == u->device_desc.product_name) {
> +        memcpy(&req->rsp_upiu.qr.data, &product_name_str_desc,
> +               product_name_str_desc.length);
> +    } else if (index == u->device_desc.serial_number) {
> +        memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
> +    } else if (index == u->device_desc.oem_id) {
> +        memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
> +    } else if (index == u->device_desc.product_revision_level) {
> +        memcpy(&req->rsp_upiu.qr.data, &product_rev_level_str_desc,
> +               product_rev_level_str_desc.length);
> +    } else {
> +        trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, index);
> +        return QUERY_RESULT_INVALID_INDEX;
> +    }
> +    return QUERY_RESULT_SUCCESS;
> +}
> +
> +static const InterconnectDescriptor interconnect_desc = {
> +    .length = sizeof(InterconnectDescriptor),
> +    .descriptor_idn = QUERY_DESC_IDN_INTERCONNECT,
> +    .bcd_unipro_version = 0x180,
> +    .bcd_mphy_version = 0x410,

What is the endianness of these two 16-bit fields?

> +};
> +
> +static QueryRespCode ufs_read_desc(UfsRequest *req)
> +{
> +    UfsHc *u = req->hc;
> +    QueryRespCode status;
> +    uint8_t idn = req->req_upiu.qr.idn;
> +    uint16_t length = be16_to_cpu(req->req_upiu.qr.length);
> +
> +    switch (idn) {
> +    case QUERY_DESC_IDN_DEVICE:
> +        memcpy(&req->rsp_upiu.qr.data, &u->device_desc, sizeof(u->device_desc));
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    case QUERY_DESC_IDN_UNIT:
> +        status = ufs_read_unit_desc(req);
> +        break;
> +    case QUERY_DESC_IDN_GEOMETRY:
> +        memcpy(&req->rsp_upiu.qr.data, &u->geometry_desc,
> +               sizeof(u->geometry_desc));
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    case QUERY_DESC_IDN_INTERCONNECT: {
> +        memcpy(&req->rsp_upiu.qr.data, &interconnect_desc,
> +               sizeof(interconnect_desc));
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    }
> +    case QUERY_DESC_IDN_STRING:
> +        status = ufs_read_string_desc(req);
> +        break;
> +    case QUERY_DESC_IDN_POWER:
> +        /* mocking of power descriptor is not supported */
> +        memset(&req->rsp_upiu.qr.data, 0, sizeof(PowerParametersDescriptor));
> +        req->rsp_upiu.qr.data[0] = sizeof(PowerParametersDescriptor);
> +        req->rsp_upiu.qr.data[1] = QUERY_DESC_IDN_POWER;
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    case QUERY_DESC_IDN_HEALTH:
> +        /* mocking of health descriptor is not supported */
> +        memset(&req->rsp_upiu.qr.data, 0, sizeof(DeviceHealthDescriptor));
> +        req->rsp_upiu.qr.data[0] = sizeof(DeviceHealthDescriptor);
> +        req->rsp_upiu.qr.data[1] = QUERY_DESC_IDN_HEALTH;
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    default:
> +        length = 0;
> +        trace_ufs_err_query_invalid_idn(req->req_upiu.qr.opcode, idn);
> +        status = QUERY_RESULT_INVALID_IDN;
> +    }
> +
> +    if (length > req->rsp_upiu.qr.data[0]) {
> +        length = req->rsp_upiu.qr.data[0];
> +    }
> +    req->rsp_upiu.qr.opcode = req->req_upiu.qr.opcode;
> +    req->rsp_upiu.qr.idn = req->req_upiu.qr.idn;
> +    req->rsp_upiu.qr.index = req->req_upiu.qr.index;
> +    req->rsp_upiu.qr.selector = req->req_upiu.qr.selector;
> +    req->rsp_upiu.qr.length = cpu_to_be16(length);
> +
> +    return status;
> +}
> +
> +static QueryRespCode ufs_exec_query_read(UfsRequest *req)
> +{
> +    QueryRespCode status;
> +    switch (req->req_upiu.qr.opcode) {
> +    case UPIU_QUERY_OPCODE_NOP:
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    case UPIU_QUERY_OPCODE_READ_DESC:
> +        status = ufs_read_desc(req);
> +        break;
> +    case UPIU_QUERY_OPCODE_READ_ATTR:
> +        status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_READ);
> +        break;
> +    case UPIU_QUERY_OPCODE_READ_FLAG:
> +        status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_READ);
> +        break;
> +    default:
> +        trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
> +        status = QUERY_RESULT_INVALID_OPCODE;
> +        break;
> +    }
> +
> +    return status;
> +}
> +
> +static QueryRespCode ufs_exec_query_write(UfsRequest *req)
> +{
> +    QueryRespCode status;
> +    switch (req->req_upiu.qr.opcode) {
> +    case UPIU_QUERY_OPCODE_NOP:
> +        status = QUERY_RESULT_SUCCESS;
> +        break;
> +    case UPIU_QUERY_OPCODE_WRITE_DESC:
> +        /* write descriptor is not supported */
> +        status = QUERY_RESULT_NOT_WRITEABLE;
> +        break;
> +    case UPIU_QUERY_OPCODE_WRITE_ATTR:
> +        status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_WRITE);
> +        break;
> +    case UPIU_QUERY_OPCODE_SET_FLAG:
> +        status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_SET);
> +        break;
> +    case UPIU_QUERY_OPCODE_CLEAR_FLAG:
> +        status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_CLEAR);
> +        break;
> +    case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
> +        status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_TOGGLE);
> +        break;
> +    default:
> +        trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
> +        status = QUERY_RESULT_INVALID_OPCODE;
> +        break;
> +    }
> +
> +    return status;
> +}
> +
> +static UfsReqResult ufs_exec_query_cmd(UfsRequest *req)
> +{
> +    uint8_t query_func = req->req_upiu.header.query_func;
> +    uint16_t data_segment_length;
> +    QueryRespCode status;
> +
> +    trace_ufs_exec_query_cmd(req->slot, req->req_upiu.qr.opcode);
> +    if (query_func == UPIU_QUERY_FUNC_STANDARD_READ_REQUEST) {
> +        status = ufs_exec_query_read(req);
> +    } else if (query_func == UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST) {
> +        status = ufs_exec_query_write(req);
> +    } else {
> +        status = QUERY_RESULT_GENERAL_FAILURE;
> +    }
> +
> +    data_segment_length = be16_to_cpu(req->rsp_upiu.qr.length);
> +    ufs_build_upiu_header(req, UPIU_TRANSACTION_QUERY_RSP, 0, status, 0,
> +                          data_segment_length);
> +
> +    if (status != QUERY_RESULT_SUCCESS) {
> +        return UFS_REQUEST_ERROR;
> +    }
> +    return UFS_REQUEST_SUCCESS;
> +}
> +
> +static void ufs_exec_req(UfsRequest *req)
> +{
> +    UfsReqResult req_result;
> +
> +    if (ufs_dma_read_upiu(req)) {
> +        return;
> +    }
> +
> +    switch (req->req_upiu.header.trans_type) {
> +    case UPIU_TRANSACTION_NOP_OUT:
> +        req_result = ufs_exec_nop_cmd(req);
> +        break;
> +    case UPIU_TRANSACTION_COMMAND:
> +        /* Not yet implemented */
> +        req_result = UFS_REQUEST_ERROR;
> +        break;
> +    case UPIU_TRANSACTION_QUERY_REQ:
> +        req_result = ufs_exec_query_cmd(req);
> +        break;
> +    default:
> +        trace_ufs_err_invalid_trans_code(req->slot,
> +                                         req->req_upiu.header.trans_type);
> +        req_result = UFS_REQUEST_ERROR;
> +    }
> +
> +    ufs_complete_req(req, req_result);
> +}
> +
> +static void ufs_process_req(void *opaque)
> +{
> +    UfsHc *u = opaque;
> +    UfsRequest *req;
> +    int slot;
> +
> +    for (slot = 0; slot < u->params.nutrs; slot++) {
> +        req = &u->req_list[slot];
> +
> +        if (req->state != UFS_REQUEST_READY) {
> +            continue;
> +        }
> +        trace_ufs_process_req(slot);
> +        req->state = UFS_REQUEST_RUNNING;
> +
> +        ufs_exec_req(req);
> +    }
> +}
> +
> +static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
> +{
> +    UfsHc *u = req->hc;
> +    assert(req->state == UFS_REQUEST_RUNNING);
> +
> +    if (req_result == UFS_REQUEST_SUCCESS) {
> +        req->utrd.header.dword_2 = OCS_SUCCESS;
> +    } else {
> +        req->utrd.header.dword_2 = OCS_INVALID_CMD_TABLE_ATTR;
> +    }

How does byte-swapping work in your patch series? This looks like native
endian but it should be little-endian.

> +
> +    trace_ufs_complete_req(req->slot);
> +    req->state = UFS_REQUEST_COMPLETE;
> +    qemu_bh_schedule(u->complete_bh);
> +}
> +
> +static void ufs_clear_req(UfsRequest *req)
> +{
> +    if (req->sg != NULL) {
> +        qemu_sglist_destroy(req->sg);
> +        g_free(req->sg);
> +        req->sg = NULL;
> +    }
> +
> +    memset(&req->utrd, 0, sizeof(req->utrd));
> +    memset(&req->req_upiu, 0, sizeof(req->req_upiu));
> +    memset(&req->rsp_upiu, 0, sizeof(req->rsp_upiu));
> +}
> +
> +static void ufs_sendback_req(void *opaque)
> +{
> +    UfsHc *u = opaque;
> +    UfsRequest *req;
> +    int slot;
> +
> +    for (slot = 0; slot < u->params.nutrs; slot++) {
> +        uint32_t is = ldl_le_p(&u->reg.is);
> +        uint32_t utrldbr = ldl_le_p(&u->reg.utrldbr);
> +        uint32_t utrlcnr = ldl_le_p(&u->reg.utrlcnr);
> +
> +        req = &u->req_list[slot];
> +
> +        if (req->state != UFS_REQUEST_COMPLETE) {
> +            continue;
> +        }
> +
> +        if (ufs_dma_write_upiu(req)) {
> +            continue;

Does this error handling work? It looks to me like a failed DMA
transaction will repeat every time ufs_sendback_req() is called instead
of stopping the request (changing req->state).

> +        }
> +
> +        /*
> +         * TODO: UTP Transfer Request Interrupt Aggregation Control is not yet
> +         * supported
> +         */
> +        if (req->utrd.header.dword_2 != OCS_SUCCESS ||
> +            req->utrd.header.dword_0 & UTP_REQ_DESC_INT_CMD) {
> +            is = FIELD_DP32(is, IS, UTRCS, 1);
> +        }
> +
> +        utrldbr &= ~(1 << slot);
> +        utrlcnr |= (1 << slot);
> +
> +        stl_le_p(&u->reg.is, is);
> +        stl_le_p(&u->reg.utrldbr, utrldbr);
> +        stl_le_p(&u->reg.utrlcnr, utrlcnr);
> +
> +        trace_ufs_sendback_req(req->slot);
> +
> +        ufs_clear_req(req);
> +        req->state = UFS_REQUEST_IDLE;
> +    }
> +
> +    ufs_irq_check(u);
> +}
> +
>  static bool ufs_check_constraints(UfsHc *u, Error **errp)
>  {
>      if (u->params.nutrs > UFS_MAX_NUTRS) {
> @@ -232,6 +1118,23 @@ static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
>      u->irq = pci_allocate_irq(pci_dev);
>  }
>  
> +static void ufs_init_state(UfsHc *u)
> +{
> +    u->req_list = g_new0(UfsRequest, u->params.nutrs);
> +
> +    for (int i = 0; i < u->params.nutrs; i++) {
> +        u->req_list[i].hc = u;
> +        u->req_list[i].slot = i;
> +        u->req_list[i].sg = NULL;
> +        u->req_list[i].state = UFS_REQUEST_IDLE;
> +    }
> +
> +    u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u,
> +                                         &DEVICE(u)->mem_reentrancy_guard);
> +    u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u,
> +                                         &DEVICE(u)->mem_reentrancy_guard);
> +}
> +
>  static void ufs_init_hc(UfsHc *u)
>  {
>      uint32_t cap = 0;
> @@ -249,6 +1152,54 @@ static void ufs_init_hc(UfsHc *u)
>      cap = FIELD_DP32(cap, CAP, CS, 0);
>      stl_le_p(&u->reg.cap, cap);
>      stl_le_p(&u->reg.ver, UFS_SPEC_VER);
> +
> +    memset(&u->device_desc, 0, sizeof(DeviceDescriptor));
> +    u->device_desc.length = sizeof(DeviceDescriptor);
> +    u->device_desc.descriptor_idn = QUERY_DESC_IDN_DEVICE;
> +    u->device_desc.device_sub_class = 0x01;
> +    u->device_desc.number_lu = 0x00;
> +    u->device_desc.number_wlu = 0x04;
> +    /* TODO: Revisit it when Power Management is implemented */
> +    u->device_desc.init_power_mode = 0x01; /* Active Mode */
> +    u->device_desc.high_priority_lun = 0x7F; /* Same Priority */
> +    u->device_desc.spec_version = cpu_to_be16(0x0310);

Can UFS_SPEC_VER be used here instead of hardcoding a magic number?

> +    u->device_desc.manufacturer_name = 0x00;
> +    u->device_desc.product_name = 0x01;
> +    u->device_desc.serial_number = 0x02;
> +    u->device_desc.oem_id = 0x03;
> +    u->device_desc.ud_0_base_offset = 0x16;
> +    u->device_desc.ud_config_p_length = 0x1A;
> +    u->device_desc.device_rtt_cap = 0x02;
> +    u->device_desc.queue_depth = u->params.nutrs;
> +    u->device_desc.product_revision_level = 0x04;
> +    u->device_desc.extended_ufs_features_support = 0x00;
> +
> +    memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
> +    u->geometry_desc.length = sizeof(GeometryDescriptor);
> +    u->geometry_desc.descriptor_idn = QUERY_DESC_IDN_GEOMETRY;
> +    u->geometry_desc.total_raw_device_capacity = 0;
> +    u->geometry_desc.max_number_lu = (UFS_MAX_LUS == 32) ? 0x1 : 0x0;
> +    u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4KB */
> +    u->geometry_desc.allocation_unit_size = 0x1; /* 4KB */
> +    u->geometry_desc.min_addr_block_size = 0x8; /* 4KB */
> +    u->geometry_desc.max_in_buffer_size = 0x8;
> +    u->geometry_desc.max_out_buffer_size = 0x8;
> +    u->geometry_desc.rpmb_read_write_size = 0x40;
> +    u->geometry_desc.data_ordering =
> +        0x0; /* out-of-order data transfer is not supported */
> +    u->geometry_desc.max_context_id_number = 0x5;
> +    u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001);
> +
> +    memset(&u->attributes, 0, sizeof(u->attributes));
> +    u->attributes.max_data_in_size = 0x08;
> +    u->attributes.max_data_out_size = 0x08;
> +    u->attributes.ref_clk_freq = 0x01; /* 26 MHz */
> +    /* configure descriptor is not supported */
> +    u->attributes.config_descr_lock = 0x01;
> +    u->attributes.max_num_of_rtt = 0x02;
> +
> +    memset(&u->flags, 0, sizeof(u->flags));
> +    u->flags.permanently_disable_fw_update = 1;
>  }
>  
>  static void ufs_realize(PCIDevice *pci_dev, Error **errp)
> @@ -259,10 +1210,24 @@ static void ufs_realize(PCIDevice *pci_dev, Error **errp)
>          return;
>      }
>  
> +    ufs_init_state(u);
>      ufs_init_hc(u);
>      ufs_init_pci(u, pci_dev);
>  }
>  
> +static void ufs_exit(PCIDevice *pci_dev)
> +{
> +    UfsHc *u = UFS(pci_dev);
> +
> +    qemu_bh_delete(u->doorbell_bh);
> +    qemu_bh_delete(u->complete_bh);
> +
> +    for (int i = 0; i < u->params.nutrs; i++) {
> +        ufs_clear_req(&u->req_list[i]);
> +    }
> +    g_free(u->req_list);
> +}
> +
>  static Property ufs_props[] = {
>      DEFINE_PROP_STRING("serial", UfsHc, params.serial),
>      DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
> @@ -281,6 +1246,7 @@ static void ufs_class_init(ObjectClass *oc, void *data)
>      PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
>  
>      pc->realize = ufs_realize;
> +    pc->exit = ufs_exit;
>      pc->class_id = PCI_CLASS_STORAGE_UFS;
>  
>      set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
> diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
> index 3c28f4e62d..5d4fd818f9 100644
> --- a/hw/ufs/ufs.h
> +++ b/hw/ufs/ufs.h
> @@ -18,6 +18,31 @@
>  #define UFS_MAX_LUS 32
>  #define UFS_LOGICAL_BLK_SIZE 4096
>  
> +typedef enum UfsRequestState {
> +    UFS_REQUEST_IDLE = 0,
> +    UFS_REQUEST_READY = 1,
> +    UFS_REQUEST_RUNNING = 2,
> +    UFS_REQUEST_COMPLETE = 3,
> +} UfsRequestState;
> +
> +typedef enum UfsReqResult {
> +    UFS_REQUEST_SUCCESS = 0,
> +    UFS_REQUEST_ERROR = 1,
> +} UfsReqResult;
> +
> +typedef struct UfsRequest {
> +    struct UfsHc *hc;
> +    UfsRequestState state;
> +    int slot;
> +
> +    UtpTransferReqDesc utrd;
> +    UtpUpiuReq req_upiu;
> +    UtpUpiuRsp rsp_upiu;
> +
> +    /* for scsi command */
> +    QEMUSGList *sg;
> +} UfsRequest;
> +
>  typedef struct UfsParams {
>      char *serial;
>      uint8_t nutrs; /* Number of UTP Transfer Request Slots */
> @@ -30,6 +55,12 @@ typedef struct UfsHc {
>      UfsReg reg;
>      UfsParams params;
>      uint32_t reg_size;
> +    UfsRequest *req_list;
> +
> +    DeviceDescriptor device_desc;
> +    GeometryDescriptor geometry_desc;
> +    Attributes attributes;
> +    Flags flags;
>  
>      qemu_irq irq;
>      QEMUBH *doorbell_bh;
> @@ -39,4 +70,18 @@ typedef struct UfsHc {
>  #define TYPE_UFS "ufs"
>  #define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
>  
> +typedef enum UfsQueryFlagPerm {
> +    UFS_QUERY_FLAG_NONE = 0x0,
> +    UFS_QUERY_FLAG_READ = 0x1,
> +    UFS_QUERY_FLAG_SET = 0x2,
> +    UFS_QUERY_FLAG_CLEAR = 0x4,
> +    UFS_QUERY_FLAG_TOGGLE = 0x8,
> +} UfsQueryFlagPerm;
> +
> +typedef enum UfsQueryAttrPerm {
> +    UFS_QUERY_ATTR_NONE = 0x0,
> +    UFS_QUERY_ATTR_READ = 0x1,
> +    UFS_QUERY_ATTR_WRITE = 0x2,
> +} UfsQueryAttrPerm;
> +
>  #endif /* HW_UFS_UFS_H */
> -- 
> 2.34.1
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  reply	other threads:[~2023-06-19  7:17 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CGME20230616065825epcms2p5682ea0edcd1fb0366a344708bea875cb@epcms2p5>
2023-06-16  6:58 ` [PATCH v2 2/3] hw/ufs: Support for Query Transfer Requests Jeuk Kim
2023-06-19  7:16   ` Stefan Hajnoczi [this message]
2023-06-19 11:04     ` Jeuk Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230619071633.GA2497337@fedora \
    --to=stefanha@redhat.com \
    --cc=fam@euphon.net \
    --cc=hreitz@redhat.com \
    --cc=jeuk20.kim@samsung.com \
    --cc=k.jensen@samsung.com \
    --cc=kwolf@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.