diff mbox series

[v2,1/3] hw/ufs: Initial commit for emulated Universal-Flash-Storage

Message ID 20230616065821epcms2p473cc98e116f5dde77baaab5626c129ec@epcms2p4 (mailing list archive)
State New, archived
Headers show
Series hw/ufs: Add Universal Flash Storage (UFS) support | expand

Commit Message

Jeuk Kim June 16, 2023, 6:58 a.m. UTC
Universal Flash Storage (UFS) is a high-performance mass storage device
with a serial interface. It is primarily used as a high-performance
data storage device for embedded applications.

This commit contains code for UFS device to be recognized
as a UFS PCI device.
Patches to handle UFS logical unit and Transfer Request will follow.

Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
---
 MAINTAINERS              |    6 +
 hw/Kconfig               |    1 +
 hw/meson.build           |    1 +
 hw/ufs/Kconfig           |    4 +
 hw/ufs/meson.build       |    1 +
 hw/ufs/trace-events      |   33 ++
 hw/ufs/trace.h           |    1 +
 hw/ufs/ufs.c             |  305 +++++++++++
 hw/ufs/ufs.h             |   42 ++
 include/block/ufs.h      | 1048 ++++++++++++++++++++++++++++++++++++++
 include/hw/pci/pci.h     |    1 +
 include/hw/pci/pci_ids.h |    1 +
 meson.build              |    1 +
 13 files changed, 1445 insertions(+)
 create mode 100644 hw/ufs/Kconfig
 create mode 100644 hw/ufs/meson.build
 create mode 100644 hw/ufs/trace-events
 create mode 100644 hw/ufs/trace.h
 create mode 100644 hw/ufs/ufs.c
 create mode 100644 hw/ufs/ufs.h
 create mode 100644 include/block/ufs.h

Comments

Stefan Hajnoczi June 16, 2023, 9:07 a.m. UTC | #1
On Fri, Jun 16, 2023 at 03:58:21PM +0900, Jeuk Kim wrote:
> Universal Flash Storage (UFS) is a high-performance mass storage device
> with a serial interface. It is primarily used as a high-performance
> data storage device for embedded applications.
> 
> This commit contains code for UFS device to be recognized
> as a UFS PCI device.
> Patches to handle UFS logical unit and Transfer Request will follow.
> 
> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
> ---
>  MAINTAINERS              |    6 +
>  hw/Kconfig               |    1 +
>  hw/meson.build           |    1 +
>  hw/ufs/Kconfig           |    4 +
>  hw/ufs/meson.build       |    1 +
>  hw/ufs/trace-events      |   33 ++
>  hw/ufs/trace.h           |    1 +
>  hw/ufs/ufs.c             |  305 +++++++++++
>  hw/ufs/ufs.h             |   42 ++
>  include/block/ufs.h      | 1048 ++++++++++++++++++++++++++++++++++++++
>  include/hw/pci/pci.h     |    1 +
>  include/hw/pci/pci_ids.h |    1 +
>  meson.build              |    1 +
>  13 files changed, 1445 insertions(+)
>  create mode 100644 hw/ufs/Kconfig
>  create mode 100644 hw/ufs/meson.build
>  create mode 100644 hw/ufs/trace-events
>  create mode 100644 hw/ufs/trace.h
>  create mode 100644 hw/ufs/ufs.c
>  create mode 100644 hw/ufs/ufs.h
>  create mode 100644 include/block/ufs.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 88b5a7ee0a..91c2bfbb09 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2231,6 +2231,12 @@ F: tests/qtest/nvme-test.c
>  F: docs/system/devices/nvme.rst
>  T: git git://git.infradead.org/qemu-nvme.git nvme-next
>  
> +ufs
> +M: Jeuk Kim <jeuk20.kim@samsung.com>
> +S: Supported
> +F: hw/ufs/*
> +F: include/block/ufs.h

Thank you for stepping up as maintainer for UFS. The responsibilities of
maintainers are to:

1. Review other people's patches that modify the code.
2. Send pull requests to the qemu.git maintainer or post your
   acknowledgement of patches so another maintainer can merge patches
   into a larger subsystem branch. For example, you could ack patches
   and Klaus could include them in his pull requests for the time being.
3. Ensure a basic level of testing and CI integration to prevent
   bitrot and regressions.

For this last point, I suggest writing a libqos test that performs
device initialization and executes some basic I/O. Not all existing
emulated storage controllers have this level of testing, but I highly
recommend having an automated test case for your new device. There is
documentation on how to write tests here:
https://qemu.readthedocs.io/en/latest/devel/qtest.html

An example test case is the AHCI (SATA) controller test:
https://gitlab.com/qemu-project/qemu/-/blob/master/tests/qtest/ahci-test.c

>  megasas
>  M: Hannes Reinecke <hare@suse.com>
>  L: qemu-block@nongnu.org
> diff --git a/hw/Kconfig b/hw/Kconfig
> index ba62ff6417..9ca7b38c31 100644
> --- a/hw/Kconfig
> +++ b/hw/Kconfig
> @@ -38,6 +38,7 @@ source smbios/Kconfig
>  source ssi/Kconfig
>  source timer/Kconfig
>  source tpm/Kconfig
> +source ufs/Kconfig
>  source usb/Kconfig
>  source virtio/Kconfig
>  source vfio/Kconfig
> diff --git a/hw/meson.build b/hw/meson.build
> index c7ac7d3d75..f01fac4617 100644
> --- a/hw/meson.build
> +++ b/hw/meson.build
> @@ -37,6 +37,7 @@ subdir('smbios')
>  subdir('ssi')
>  subdir('timer')
>  subdir('tpm')
> +subdir('ufs')
>  subdir('usb')
>  subdir('vfio')
>  subdir('virtio')
> diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig
> new file mode 100644
> index 0000000000..b7b3392e85
> --- /dev/null
> +++ b/hw/ufs/Kconfig
> @@ -0,0 +1,4 @@
> +config UFS_PCI
> +    bool
> +    default y if PCI_DEVICES
> +    depends on PCI
> diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build
> new file mode 100644
> index 0000000000..c1d90eeea6
> --- /dev/null
> +++ b/hw/ufs/meson.build
> @@ -0,0 +1 @@
> +softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c'))
> diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
> new file mode 100644
> index 0000000000..17793929b1
> --- /dev/null
> +++ b/hw/ufs/trace-events
> @@ -0,0 +1,33 @@
> +# ufs.c
> +ufs_irq_raise(void) "INTx"
> +ufs_irq_lower(void) "INTx"
> +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
> +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
> +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32""
> +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
> +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
> +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
> +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32""
> +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8""
> +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8""
> +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32""
> +
> +# error condition
> +ufs_err_memory_allocation(void) "failed to allocate memory"
> +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
> +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""
> +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
> +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
> +ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64""
> +ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy"
> +ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported"
> +ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid"
> +ufs_err_scsi_cmd_invalid_lun(uint8_t lun) "scsi command has invalid lun: 0x%"PRIx8""
> +ufs_err_query_flag_not_readable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to read"
> +ufs_err_query_flag_not_writable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to write"
> +ufs_err_query_attr_not_readable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to read"
> +ufs_err_query_attr_not_writable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to write"
> +ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. opcode: 0x%"PRIx8""
> +ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8""
> +ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8""
> +ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8""
> diff --git a/hw/ufs/trace.h b/hw/ufs/trace.h
> new file mode 100644
> index 0000000000..2dbd6397c3
> --- /dev/null
> +++ b/hw/ufs/trace.h
> @@ -0,0 +1 @@
> +#include "trace/trace-hw_ufs.h"
> diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
> new file mode 100644
> index 0000000000..9dba1073a8
> --- /dev/null
> +++ b/hw/ufs/ufs.c
> @@ -0,0 +1,305 @@
> +/*
> + * QEMU Universal Flash Storage (UFS) Controller
> + *
> + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
> + *
> + * Written by Jeuk Kim <jeuk20.kim@samsung.com>
> + *
> + * This code is licensed under the GNU GPL v2 or later.

Please use SPDX license identifiers in new files to make it easier for
license tooling:

 * SPDX-License-Identifier: GPL-2.0-or-later

> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "migration/vmstate.h"
> +#include "trace.h"
> +#include "ufs.h"
> +
> +/* The QEMU-UFS device follows spec version 3.1 */
> +#define UFS_SPEC_VER 0x00000310
> +#define UFS_MAX_NUTRS 32
> +#define UFS_MAX_NUTMRS 8
> +
> +static void ufs_irq_check(UfsHc *u)
> +{
> +    PCIDevice *pci = PCI_DEVICE(u);
> +    uint32_t is = ldl_le_p(&u->reg.is);
> +    uint32_t ie = ldl_le_p(&u->reg.ie);
> +
> +    if ((is & UFS_INTR_MASK) & ie) {
> +        trace_ufs_irq_raise();
> +        pci_irq_assert(pci);
> +    } else {
> +        trace_ufs_irq_lower();
> +        pci_irq_deassert(pci);
> +    }
> +}

Old-school PCI interrupts! I'm surprised there are no MSI-X interrupts.
:)

> +
> +static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
> +{
> +    uint32_t is = ldl_le_p(&u->reg.is);
> +    uint32_t hcs = ldl_le_p(&u->reg.hcs);
> +    uint32_t ucmdarg1 = ldl_le_p(&u->reg.ucmdarg1);
> +    uint32_t ucmdarg2 = ldl_le_p(&u->reg.ucmdarg2);
> +    uint32_t ucmdarg3 = ldl_le_p(&u->reg.ucmdarg3);
> +
> +    trace_ufs_process_uiccmd(val, ucmdarg1, ucmdarg2, ucmdarg3);
> +    /*
> +     * Only the essential uic commands for running drivers on Linux and Windows
> +     * are implemented.
> +     */
> +    switch (val) {
> +    case UIC_CMD_DME_LINK_STARTUP:
> +        hcs = FIELD_DP32(hcs, HCS, DP, 1);
> +        hcs = FIELD_DP32(hcs, HCS, UTRLRDY, 1);
> +        hcs = FIELD_DP32(hcs, HCS, UTMRLRDY, 1);
> +        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
> +        break;
> +    /* TODO: Revisit it when Power Management is implemented */
> +    case UIC_CMD_DME_HIBER_ENTER:
> +        is = FIELD_DP32(is, IS, UHES, 1);
> +        hcs = FIELD_DP32(hcs, HCS, UPMCRS, PWR_LOCAL);
> +        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
> +        break;
> +    case UIC_CMD_DME_HIBER_EXIT:
> +        is = FIELD_DP32(is, IS, UHXS, 1);
> +        hcs = FIELD_DP32(hcs, HCS, UPMCRS, PWR_LOCAL);
> +        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
> +        break;
> +    default:
> +        ucmdarg2 = UIC_CMD_RESULT_FAILURE;
> +    }
> +
> +    is = FIELD_DP32(is, IS, UCCS, 1);
> +
> +    stl_le_p(&u->reg.is, is);
> +    stl_le_p(&u->reg.hcs, hcs);
> +    stl_le_p(&u->reg.ucmdarg1, ucmdarg1);
> +    stl_le_p(&u->reg.ucmdarg2, ucmdarg2);
> +    stl_le_p(&u->reg.ucmdarg3, ucmdarg3);
> +
> +    ufs_irq_check(u);
> +}
> +
> +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 utrlcnr = ldl_le_p(&u->reg.utrlcnr);
> +    uint32_t utrlba, utmrlba;
> +
> +    switch (offset) {
> +    case A_IS:
> +        is &= ~data;
> +        stl_le_p(&u->reg.is, is);
> +        ufs_irq_check(u);
> +        break;
> +    case A_IE:
> +        stl_le_p(&u->reg.ie, data);
> +        ufs_irq_check(u);
> +        break;
> +    case A_HCE:
> +        if (!FIELD_EX32(hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) {
> +            hcs = FIELD_DP32(hcs, HCS, UCRDY, 1);
> +            hce = FIELD_DP32(hce, HCE, HCE, 1);
> +            stl_le_p(&u->reg.hcs, hcs);
> +            stl_le_p(&u->reg.hce, hce);
> +        } else if (FIELD_EX32(hce, HCE, HCE) && !FIELD_EX32(data, HCE, HCE)) {
> +            hcs = 0;
> +            hce = FIELD_DP32(hce, HCE, HCE, 0);
> +            stl_le_p(&u->reg.hcs, hcs);
> +            stl_le_p(&u->reg.hce, hce);
> +        }
> +        break;
> +    case A_UTRLBA:
> +        utrlba = data & R_UTRLBA_UTRLBA_MASK;
> +        stl_le_p(&u->reg.utrlba, utrlba);
> +        break;
> +    case A_UTRLBAU:
> +        stl_le_p(&u->reg.utrlbau, data);
> +        break;
> +    case A_UTRLDBR:
> +        /* Not yet supported */
> +        break;
> +    case A_UTRLRSR:
> +        stl_le_p(&u->reg.utrlrsr, data);
> +        break;
> +    case A_UTRLCNR:
> +        utrlcnr &= ~data;
> +        stl_le_p(&u->reg.utrlcnr, utrlcnr);
> +        break;
> +    case A_UTMRLBA:
> +        utmrlba = data & R_UTMRLBA_UTMRLBA_MASK;
> +        stl_le_p(&u->reg.utmrlba, utmrlba);
> +        break;
> +    case A_UTMRLBAU:
> +        stl_le_p(&u->reg.utmrlbau, data);
> +        break;
> +    case A_UICCMD:
> +        ufs_process_uiccmd(u, data);
> +        break;
> +    case A_UCMDARG1:
> +        stl_le_p(&u->reg.ucmdarg1, data);
> +        break;
> +    case A_UCMDARG2:
> +        stl_le_p(&u->reg.ucmdarg2, data);
> +        break;
> +    case A_UCMDARG3:
> +        stl_le_p(&u->reg.ucmdarg3, data);
> +        break;
> +    case A_UTRLCLR:
> +    case A_UTMRLDBR:
> +    case A_UTMRLCLR:
> +    case A_UTMRLRSR:
> +        trace_ufs_err_unsupport_register_offset(offset);
> +        break;
> +    default:
> +        trace_ufs_err_invalid_register_offset(offset);
> +        break;
> +    }
> +}
> +
> +static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    UfsHc *u = (UfsHc *)opaque;
> +    uint8_t *ptr = (uint8_t *)&u->reg;
> +    uint64_t value;
> +
> +    if (addr > sizeof(u->reg) - size) {
> +        trace_ufs_err_invalid_register_offset(addr);
> +        return 0;
> +    }
> +
> +    value = ldn_le_p(ptr + addr, size);
> +    trace_ufs_mmio_read(addr, value, size);
> +    return value;
> +}
> +
> +static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
> +                           unsigned size)
> +{
> +    UfsHc *u = (UfsHc *)opaque;
> +
> +    if (addr > sizeof(u->reg) - size) {
> +        trace_ufs_err_invalid_register_offset(addr);
> +        return;
> +    }
> +
> +    trace_ufs_mmio_write(addr, data, size);
> +    ufs_write_reg(u, addr, data, size);
> +}
> +
> +static const MemoryRegionOps ufs_mmio_ops = {
> +    .read = ufs_mmio_read,
> +    .write = ufs_mmio_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static bool ufs_check_constraints(UfsHc *u, Error **errp)
> +{
> +    if (u->params.nutrs > UFS_MAX_NUTRS) {
> +        error_setg(errp, "nutrs must be less than %d", UFS_MAX_NUTRS);

Off-by-one: the if statement condition checks for <= but the error
message says <.

> +        return false;
> +    }
> +
> +    if (u->params.nutmrs > UFS_MAX_NUTMRS) {
> +        error_setg(errp, "nutmrs must be less than %d", UFS_MAX_NUTMRS);

Same here.

> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
> +{
> +    uint8_t *pci_conf = pci_dev->config;
> +
> +    pci_conf[PCI_INTERRUPT_PIN] = 1;
> +    pci_config_set_prog_interface(pci_conf, 0x1);
> +
> +    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT);
> +    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REDHAT_UFS);
> +
> +    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_UFS);
> +
> +    memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
> +                          u->reg_size);
> +    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
> +    u->irq = pci_allocate_irq(pci_dev);
> +}
> +
> +static void ufs_init_hc(UfsHc *u)
> +{
> +    uint32_t cap = 0;
> +
> +    u->reg_size = pow2ceil(sizeof(UfsReg));
> +
> +    memset(&u->reg, 0, sizeof(u->reg));
> +    cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
> +    cap = FIELD_DP32(cap, CAP, RTT, 2);
> +    cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
> +    cap = FIELD_DP32(cap, CAP, AUTOH8, 0);
> +    cap = FIELD_DP32(cap, CAP, 64AS, 1);
> +    cap = FIELD_DP32(cap, CAP, OODDS, 0);
> +    cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
> +    cap = FIELD_DP32(cap, CAP, CS, 0);
> +    stl_le_p(&u->reg.cap, cap);
> +    stl_le_p(&u->reg.ver, UFS_SPEC_VER);
> +}
> +
> +static void ufs_realize(PCIDevice *pci_dev, Error **errp)
> +{
> +    UfsHc *u = UFS(pci_dev);
> +
> +    if (!ufs_check_constraints(u, errp)) {
> +        return;
> +    }
> +
> +    ufs_init_hc(u);
> +    ufs_init_pci(u, pci_dev);
> +}
> +
> +static Property ufs_props[] = {
> +    DEFINE_PROP_STRING("serial", UfsHc, params.serial),
> +    DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
> +    DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static const VMStateDescription ufs_vmstate = {
> +    .name = "ufs",
> +    .unmigratable = 1,
> +};
> +
> +static void ufs_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
> +
> +    pc->realize = ufs_realize;
> +    pc->class_id = PCI_CLASS_STORAGE_UFS;

Why does ufs_init_pci() also call pci_config_set_class(pci_conf,
PCI_CLASS_STORAGE_UFS)? The following values can be removed from
ufs_init_pci() and initialized here instead:

  pc->vendor_id = PCI_VENDOR_ID_REDHAT;
  pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
  pc->class_id = PCI_CLASS_STORAGE_UFS;

> +
> +    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
> +    dc->desc = "Universal Flash Storage";
> +    device_class_set_props(dc, ufs_props);
> +    dc->vmsd = &ufs_vmstate;
> +}
> +
> +static const TypeInfo ufs_info = {
> +    .name = TYPE_UFS,
> +    .parent = TYPE_PCI_DEVICE,
> +    .class_init = ufs_class_init,
> +    .instance_size = sizeof(UfsHc),
> +    .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
> +};
> +
> +static void ufs_register_types(void)
> +{
> +    type_register_static(&ufs_info);
> +}
> +
> +type_init(ufs_register_types)
> diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
> new file mode 100644
> index 0000000000..3c28f4e62d
> --- /dev/null
> +++ b/hw/ufs/ufs.h
> @@ -0,0 +1,42 @@
> +/*
> + * QEMU UFS
> + *
> + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
> + *
> + * Written by Jeuk Kim <jeuk20.kim@samsung.com>
> + *
> + * This code is licensed under the GNU GPL v2 or later.
> + */
> +
> +#ifndef HW_UFS_UFS_H
> +#define HW_UFS_UFS_H
> +
> +#include "hw/pci/pci_device.h"
> +#include "hw/scsi/scsi.h"
> +#include "block/ufs.h"
> +
> +#define UFS_MAX_LUS 32
> +#define UFS_LOGICAL_BLK_SIZE 4096
> +
> +typedef struct UfsParams {
> +    char *serial;
> +    uint8_t nutrs; /* Number of UTP Transfer Request Slots */
> +    uint8_t nutmrs; /* Number of UTP Task Management Request Slots */
> +} UfsParams;
> +
> +typedef struct UfsHc {
> +    PCIDevice parent_obj;
> +    MemoryRegion iomem;
> +    UfsReg reg;
> +    UfsParams params;
> +    uint32_t reg_size;
> +
> +    qemu_irq irq;
> +    QEMUBH *doorbell_bh;
> +    QEMUBH *complete_bh;
> +} UfsHc;
> +
> +#define TYPE_UFS "ufs"
> +#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
> +
> +#endif /* HW_UFS_UFS_H */
> diff --git a/include/block/ufs.h b/include/block/ufs.h
> new file mode 100644
> index 0000000000..0e4c0e5d93
> --- /dev/null
> +++ b/include/block/ufs.h
> @@ -0,0 +1,1048 @@
> +#ifndef BLOCK_UFS_H
> +#define BLOCK_UFS_H
> +
> +#include "hw/registerfields.h"
> +
> +typedef struct QEMU_PACKED UfsReg {
> +    uint32_t cap;
> +    uint32_t rsvd0;
> +    uint32_t ver;
> +    uint32_t rsvd1;
> +    uint32_t hcpid;
> +    uint32_t hcmid;
> +    uint32_t ahit;
> +    uint32_t rsvd2;
> +    uint32_t is;
> +    uint32_t ie;
> +    uint32_t rsvd3[2];
> +    uint32_t hcs;
> +    uint32_t hce;
> +    uint32_t uecpa;
> +    uint32_t uecdl;
> +    uint32_t uecn;
> +    uint32_t uect;
> +    uint32_t uecdme;
> +    uint32_t utriacr;
> +    uint32_t utrlba;
> +    uint32_t utrlbau;
> +    uint32_t utrldbr;
> +    uint32_t utrlclr;
> +    uint32_t utrlrsr;
> +    uint32_t utrlcnr;
> +    uint32_t rsvd4[2];
> +    uint32_t utmrlba;
> +    uint32_t utmrlbau;
> +    uint32_t utmrldbr;
> +    uint32_t utmrlclr;
> +    uint32_t utmrlrsr;
> +    uint32_t rsvd5[3];
> +    uint32_t uiccmd;
> +    uint32_t ucmdarg1;
> +    uint32_t ucmdarg2;
> +    uint32_t ucmdarg3;
> +    uint32_t rsvd6[4];
> +    uint32_t rsvd7[4];
> +    uint32_t rsvd8[16];
> +    uint32_t ccap;
> +} UfsReg;
> +
> +REG32(CAP, offsetof(UfsReg, cap))
> +    FIELD(CAP, NUTRS, 0, 5)
> +    FIELD(CAP, RTT, 8, 8)
> +    FIELD(CAP, NUTMRS, 16, 3)
> +    FIELD(CAP, AUTOH8, 23, 1)
> +    FIELD(CAP, 64AS, 24, 1)
> +    FIELD(CAP, OODDS, 25, 1)
> +    FIELD(CAP, UICDMETMS, 26, 1)
> +    FIELD(CAP, CS, 28, 1)
> +REG32(VER, offsetof(UfsReg, ver))
> +REG32(HCPID, offsetof(UfsReg, hcpid))
> +REG32(HCMID, offsetof(UfsReg, hcmid))
> +REG32(AHIT, offsetof(UfsReg, ahit))
> +REG32(IS, offsetof(UfsReg, is))
> +    FIELD(IS, UTRCS, 0, 1)
> +    FIELD(IS, UDEPRI, 1, 1)
> +    FIELD(IS, UE, 2, 1)
> +    FIELD(IS, UTMS, 3, 1)
> +    FIELD(IS, UPMS, 4, 1)
> +    FIELD(IS, UHXS, 5, 1)
> +    FIELD(IS, UHES, 6, 1)
> +    FIELD(IS, ULLS, 7, 1)
> +    FIELD(IS, ULSS, 8, 1)
> +    FIELD(IS, UTMRCS, 9, 1)
> +    FIELD(IS, UCCS, 10, 1)
> +    FIELD(IS, DFES, 11, 1)
> +    FIELD(IS, UTPES, 12, 1)
> +    FIELD(IS, HCFES, 16, 1)
> +    FIELD(IS, SBFES, 17, 1)
> +    FIELD(IS, CEFES, 18, 1)
> +REG32(IE, offsetof(UfsReg, ie))
> +REG32(HCS, offsetof(UfsReg, hcs))
> +    FIELD(HCS, DP, 0, 1)
> +    FIELD(HCS, UTRLRDY, 1, 1)
> +    FIELD(HCS, UTMRLRDY, 2, 1)
> +    FIELD(HCS, UCRDY, 3, 1)
> +    FIELD(HCS, UPMCRS, 8, 3)
> +REG32(HCE, offsetof(UfsReg, hce))
> +    FIELD(HCE, HCE, 0, 1)
> +    FIELD(HCE, CGE, 1, 1)
> +REG32(UECPA, offsetof(UfsReg, uecpa))
> +REG32(UECDL, offsetof(UfsReg, uecdl))
> +REG32(UECN, offsetof(UfsReg, uecn))
> +REG32(UECT, offsetof(UfsReg, uect))
> +REG32(UECDME, offsetof(UfsReg, uecdme))
> +REG32(UTRIACR, offsetof(UfsReg, utriacr))
> +REG32(UTRLBA, offsetof(UfsReg, utrlba))
> +    FIELD(UTRLBA, UTRLBA, 9, 22)
> +REG32(UTRLBAU, offsetof(UfsReg, utrlbau))
> +REG32(UTRLDBR, offsetof(UfsReg, utrldbr))
> +REG32(UTRLCLR, offsetof(UfsReg, utrlclr))
> +REG32(UTRLRSR, offsetof(UfsReg, utrlrsr))
> +REG32(UTRLCNR, offsetof(UfsReg, utrlcnr))
> +REG32(UTMRLBA, offsetof(UfsReg, utmrlba))
> +    FIELD(UTMRLBA, UTMRLBA, 9, 22)
> +REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau))
> +REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr))
> +REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr))
> +REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr))
> +REG32(UICCMD, offsetof(UfsReg, uiccmd))
> +REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1))
> +REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2))
> +REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3))
> +REG32(CCAP, offsetof(UfsReg, ccap))
> +
> +#define UFS_INTR_MASK                                    \
> +    ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
> +     (1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \
> +     (1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) |   \
> +     (1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \
> +     (1 << R_IS_ULLS_SHIFT) | (1 << R_IS_UHES_SHIFT) |   \
> +     (1 << R_IS_UHXS_SHIFT) | (1 << R_IS_UPMS_SHIFT) |   \
> +     (1 << R_IS_UTMS_SHIFT) | (1 << R_IS_UE_SHIFT) |     \
> +     (1 << R_IS_UDEPRI_SHIFT) | (1 << R_IS_UTRCS_SHIFT))
> +
> +#define UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT 24
> +#define UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK 0xff
> +#define UFS_UPIU_HEADER_TRANSACTION_TYPE(dword0)                       \
> +    ((be32_to_cpu(dword0) >> UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT) & \
> +     UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK)
> +
> +#define UFS_UPIU_HEADER_QUERY_FUNC_SHIFT 16
> +#define UFS_UPIU_HEADER_QUERY_FUNC_MASK 0xff
> +#define UFS_UPIU_HEADER_QUERY_FUNC(dword1)                       \
> +    ((be32_to_cpu(dword1) >> UFS_UPIU_HEADER_QUERY_FUNC_SHIFT) & \
> +     UFS_UPIU_HEADER_QUERY_FUNC_MASK)
> +
> +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT 0
> +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK 0xffff
> +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH(dword2)                       \
> +    ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \
> +     UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK)
> +
> +typedef struct QEMU_PACKED DeviceDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint8_t device;
> +    uint8_t device_class;
> +    uint8_t device_sub_class;
> +    uint8_t protocol;
> +    uint8_t number_lu;
> +    uint8_t number_wlu;
> +    uint8_t boot_enable;
> +    uint8_t descr_access_en;
> +    uint8_t init_power_mode;
> +    uint8_t high_priority_lun;
> +    uint8_t secure_removal_type;
> +    uint8_t security_lu;
> +    uint8_t background_ops_term_lat;
> +    uint8_t init_active_icc_level;
> +    uint16_t spec_version;
> +    uint16_t manufacture_date;
> +    uint8_t manufacturer_name;
> +    uint8_t product_name;
> +    uint8_t serial_number;
> +    uint8_t oem_id;
> +    uint16_t manufacturer_id;
> +    uint8_t ud_0_base_offset;
> +    uint8_t ud_config_p_length;
> +    uint8_t device_rtt_cap;
> +    uint16_t periodic_rtc_update;
> +    uint8_t ufs_features_support;
> +    uint8_t ffu_timeout;
> +    uint8_t queue_depth;
> +    uint16_t device_version;
> +    uint8_t num_secure_wp_area;
> +    uint32_t psa_max_data_size;
> +    uint8_t psa_state_timeout;
> +    uint8_t product_revision_level;
> +    uint8_t reserved[36];
> +    uint32_t extended_ufs_features_support;
> +    uint8_t write_booster_buffer_preserve_user_space_en;
> +    uint8_t write_booster_buffer_type;
> +    uint32_t num_shared_write_booster_buffer_alloc_units;
> +} DeviceDescriptor;
> +
> +typedef struct QEMU_PACKED GeometryDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint8_t media_technology;
> +    uint8_t reserved;
> +    uint64_t total_raw_device_capacity;
> +    uint8_t max_number_lu;
> +    uint32_t segment_size;
> +    uint8_t allocation_unit_size;
> +    uint8_t min_addr_block_size;
> +    uint8_t optimal_read_block_size;
> +    uint8_t optimal_write_block_size;
> +    uint8_t max_in_buffer_size;
> +    uint8_t max_out_buffer_size;
> +    uint8_t rpmb_read_write_size;
> +    uint8_t dynamic_capacity_resource_policy;
> +    uint8_t data_ordering;
> +    uint8_t max_context_id_number;
> +    uint8_t sys_data_tag_unit_size;
> +    uint8_t sys_data_tag_res_size;
> +    uint8_t supported_sec_r_types;
> +    uint16_t supported_memory_types;
> +    uint32_t system_code_max_n_alloc_u;
> +    uint16_t system_code_cap_adj_fac;
> +    uint32_t non_persist_max_n_alloc_u;
> +    uint16_t non_persist_cap_adj_fac;
> +    uint32_t enhanced_1_max_n_alloc_u;
> +    uint16_t enhanced_1_cap_adj_fac;
> +    uint32_t enhanced_2_max_n_alloc_u;
> +    uint16_t enhanced_2_cap_adj_fac;
> +    uint32_t enhanced_3_max_n_alloc_u;
> +    uint16_t enhanced_3_cap_adj_fac;
> +    uint32_t enhanced_4_max_n_alloc_u;
> +    uint16_t enhanced_4_cap_adj_fac;
> +    uint32_t optimal_logical_block_size;
> +    uint8_t reserved2[7];
> +    uint32_t write_booster_buffer_max_n_alloc_units;
> +    uint8_t device_max_write_booster_l_us;
> +    uint8_t write_booster_buffer_cap_adj_fac;
> +    uint8_t supported_write_booster_buffer_user_space_reduction_types;
> +    uint8_t supported_write_booster_buffer_types;
> +} GeometryDescriptor;
> +
> +#define UFS_GEOMETRY_CAPACITY_SHIFT 9
> +
> +typedef struct QEMU_PACKED UnitDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint8_t unit_index;
> +    uint8_t lu_enable;
> +    uint8_t boot_lun_id;
> +    uint8_t lu_write_protect;
> +    uint8_t lu_queue_depth;
> +    uint8_t psa_sensitive;
> +    uint8_t memory_type;
> +    uint8_t data_reliability;
> +    uint8_t logical_block_size;
> +    uint64_t logical_block_count;
> +    uint32_t erase_block_size;
> +    uint8_t provisioning_type;
> +    uint64_t phy_mem_resource_count;
> +    uint16_t context_capabilities;
> +    uint8_t large_unit_granularity_m1;
> +    uint8_t reserved[6];
> +    uint32_t lu_num_write_booster_buffer_alloc_units;
> +} UnitDescriptor;
> +
> +typedef struct QEMU_PACKED RpmbUnitDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint8_t unit_index;
> +    uint8_t lu_enable;
> +    uint8_t boot_lun_id;
> +    uint8_t lu_write_protect;
> +    uint8_t lu_queue_depth;
> +    uint8_t psa_sensitive;
> +    uint8_t memory_type;
> +    uint8_t reserved;
> +    uint8_t logical_block_size;
> +    uint64_t logical_block_count;
> +    uint32_t erase_block_size;
> +    uint8_t provisioning_type;
> +    uint64_t phy_mem_resource_count;
> +    uint8_t reserved2[3];
> +} RpmbUnitDescriptor;
> +
> +typedef struct QEMU_PACKED PowerParametersDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint16_t active_icc_levels_vcc[16];
> +    uint16_t active_icc_levels_vccq[16];
> +    uint16_t active_icc_levels_vccq_2[16];
> +} PowerParametersDescriptor;
> +
> +typedef struct QEMU_PACKED InterconnectDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint16_t bcd_unipro_version;
> +    uint16_t bcd_mphy_version;
> +} InterconnectDescriptor;
> +
> +typedef struct QEMU_PACKED StringDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint16_t UC[126];
> +} StringDescriptor;
> +
> +typedef struct QEMU_PACKED DeviceHealthDescriptor {
> +    uint8_t length;
> +    uint8_t descriptor_idn;
> +    uint8_t pre_eol_info;
> +    uint8_t device_life_time_est_a;
> +    uint8_t device_life_time_est_b;
> +    uint8_t vendor_prop_info[32];
> +    uint32_t refresh_total_count;
> +    uint32_t refresh_progress;
> +} DeviceHealthDescriptor;
> +
> +typedef struct QEMU_PACKED Flags {
> +    uint8_t reserved;
> +    uint8_t device_init;
> +    uint8_t permanent_wp_en;
> +    uint8_t power_on_wp_en;
> +    uint8_t background_ops_en;
> +    uint8_t device_life_span_mode_en;
> +    uint8_t purge_enable;
> +    uint8_t refresh_enable;
> +    uint8_t phy_resource_removal;
> +    uint8_t busy_rtc;
> +    uint8_t reserved2;
> +    uint8_t permanently_disable_fw_update;
> +    uint8_t reserved3[2];
> +    uint8_t wb_en;
> +    uint8_t wb_buffer_flush_en;
> +    uint8_t wb_buffer_flush_during_hibernate;
> +    uint8_t reserved4[2];
> +} Flags;
> +
> +typedef struct Attributes {
> +    uint8_t boot_lun_en;
> +    uint8_t reserved;
> +    uint8_t current_power_mode;
> +    uint8_t active_icc_level;
> +    uint8_t out_of_order_data_en;
> +    uint8_t background_op_status;
> +    uint8_t purge_status;
> +    uint8_t max_data_in_size;
> +    uint8_t max_data_out_size;
> +    uint32_t dyn_cap_needed;
> +    uint8_t ref_clk_freq;
> +    uint8_t config_descr_lock;
> +    uint8_t max_num_of_rtt;
> +    uint16_t exception_event_control;
> +    uint16_t exception_event_status;
> +    uint32_t seconds_passed;
> +    uint16_t context_conf;
> +    uint8_t device_ffu_status;
> +    uint8_t psa_state;
> +    uint32_t psa_data_size;
> +    uint8_t ref_clk_gating_wait_time;
> +    uint8_t device_case_rough_temperaure;
> +    uint8_t device_too_high_temp_boundary;
> +    uint8_t device_too_low_temp_boundary;
> +    uint8_t throttling_status;
> +    uint8_t wb_buffer_flush_status;
> +    uint8_t available_wb_buffer_size;
> +    uint8_t wb_buffer_life_time_est;
> +    uint32_t current_wb_buffer_size;
> +    uint8_t refresh_status;
> +    uint8_t refresh_freq;
> +    uint8_t refresh_unit;
> +    uint8_t refresh_method;
> +} Attributes;
> +
> +#define UFS_TRANSACTION_SPECIFIC_FIELD_SIZE 20
> +#define UFS_MAX_QUERY_DATA_SIZE 256
> +
> +/* Command response result code */
> +typedef enum CommandRespCode {
> +    COMMAND_RESULT_SUCESS = 0x00,
> +    COMMAND_RESULT_FAIL = 0x01,
> +} CommandRespCode;
> +
> +enum {
> +    UFS_UPIU_FLAG_UNDERFLOW = 0x20,
> +    UFS_UPIU_FLAG_OVERFLOW = 0x40,
> +};
> +
> +typedef struct QEMU_PACKED UtpUpiuHeader {
> +    uint8_t trans_type;
> +    uint8_t flags;
> +    uint8_t lun;
> +    uint8_t task_tag;
> +    uint8_t iid_cmd_set_type;
> +    uint8_t query_func;
> +    uint8_t response;
> +    uint8_t scsi_status;
> +    uint8_t ehs_len;
> +    uint8_t device_inf;
> +    uint16_t data_segment_length;
> +} UtpUpiuHeader;
> +
> +/*
> + * The code below is copied from the linux kernel
> + * ("include/uapi/scsi/scsi_bsg_ufs.h") and modified to fit the qemu style.
> + */
> +
> +typedef struct QEMU_PACKED UtpUpiuQuery {
> +    uint8_t opcode;
> +    uint8_t idn;
> +    uint8_t index;
> +    uint8_t selector;
> +    uint16_t reserved_osf;
> +    uint16_t length;
> +    uint32_t value;
> +    uint32_t reserved[2];
> +    /* EHS length should be 0. We don't have to worry about EHS area. */
> +    uint8_t data[UFS_MAX_QUERY_DATA_SIZE];
> +} UtpUpiuQuery;
> +
> +#define UFS_CDB_SIZE 16
> +
> +/*
> + * struct UtpUpiuCmd - Command UPIU structure
> + * @data_transfer_len: Data Transfer Length DW-3
> + * @cdb: Command Descriptor Block CDB DW-4 to DW-7
> + */
> +typedef struct QEMU_PACKED UtpUpiuCmd {
> +    uint32_t exp_data_transfer_len;
> +    __uint8_t cdb[UFS_CDB_SIZE];

Why is this __uint8_t instead of uint8_t? Was the code copied from
another codebase and is the license GPLv2-or-later (this header file
doesn't contain a license identifier)?

> +} UtpUpiuCmd;
> +
> +/*
> + * struct UtpUpiuReq - general upiu request structure
> + * @header:UPIU header structure DW-0 to DW-2
> + * @sc: fields structure for scsi command DW-3 to DW-7
> + * @qr: fields structure for query request DW-3 to DW-7
> + * @uc: use utp_upiu_query to host the 4 dwords of uic command
> + */
> +typedef struct QEMU_PACKED UtpUpiuReq {
> +    UtpUpiuHeader header;
> +    union {
> +        UtpUpiuCmd sc;
> +        UtpUpiuQuery qr;
> +    };
> +} UtpUpiuReq;
> +
> +/*
> + * The code below is copied from the linux kernel ("include/ufs/ufshci.h") and
> + * modified to fit the qemu style.
> + */
> +
> +enum {
> +    PWR_OK = 0x0,
> +    PWR_LOCAL = 0x01,
> +    PWR_REMOTE = 0x02,
> +    PWR_BUSY = 0x03,
> +    PWR_ERROR_CAP = 0x04,
> +    PWR_FATAL_ERROR = 0x05,
> +};
> +
> +/* UIC Commands */
> +enum uic_cmd_dme {
> +    UIC_CMD_DME_GET = 0x01,
> +    UIC_CMD_DME_SET = 0x02,
> +    UIC_CMD_DME_PEER_GET = 0x03,
> +    UIC_CMD_DME_PEER_SET = 0x04,
> +    UIC_CMD_DME_POWERON = 0x10,
> +    UIC_CMD_DME_POWEROFF = 0x11,
> +    UIC_CMD_DME_ENABLE = 0x12,
> +    UIC_CMD_DME_RESET = 0x14,
> +    UIC_CMD_DME_END_PT_RST = 0x15,
> +    UIC_CMD_DME_LINK_STARTUP = 0x16,
> +    UIC_CMD_DME_HIBER_ENTER = 0x17,
> +    UIC_CMD_DME_HIBER_EXIT = 0x18,
> +    UIC_CMD_DME_TEST_MODE = 0x1A,
> +};
> +
> +/* UIC Config result code / Generic error code */
> +enum {
> +    UIC_CMD_RESULT_SUCCESS = 0x00,
> +    UIC_CMD_RESULT_INVALID_ATTR = 0x01,
> +    UIC_CMD_RESULT_FAILURE = 0x01,
> +    UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
> +    UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
> +    UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
> +    UIC_CMD_RESULT_BAD_INDEX = 0x05,
> +    UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
> +    UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
> +    UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
> +    UIC_CMD_RESULT_BUSY = 0x09,
> +    UIC_CMD_RESULT_DME_FAILURE = 0x0A,
> +};
> +
> +#define MASK_UIC_COMMAND_RESULT 0xFF
> +
> +/*
> + * Request Descriptor Definitions
> + */
> +
> +/* Transfer request command type */
> +enum {
> +    UTP_CMD_TYPE_SCSI = 0x0,
> +    UTP_CMD_TYPE_UFS = 0x1,
> +    UTP_CMD_TYPE_DEV_MANAGE = 0x2,
> +};
> +
> +/* To accommodate UFS2.0 required Command type */
> +enum {
> +    UTP_CMD_TYPE_UFS_STORAGE = 0x1,
> +};
> +
> +enum {
> +    UTP_SCSI_COMMAND = 0x00000000,
> +    UTP_NATIVE_UFS_COMMAND = 0x10000000,
> +    UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
> +    UTP_REQ_DESC_INT_CMD = 0x01000000,
> +    UTP_REQ_DESC_CRYPTO_ENABLE_CMD = 0x00800000,
> +};
> +
> +/* UTP Transfer Request Data Direction (DD) */
> +enum {
> +    UTP_NO_DATA_TRANSFER = 0x00000000,
> +    UTP_HOST_TO_DEVICE = 0x02000000,
> +    UTP_DEVICE_TO_HOST = 0x04000000,
> +};
> +
> +/* Overall command status values */
> +enum UtpOcsCodes {
> +    OCS_SUCCESS = 0x0,
> +    OCS_INVALID_CMD_TABLE_ATTR = 0x1,
> +    OCS_INVALID_PRDT_ATTR = 0x2,
> +    OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
> +    OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
> +    OCS_PEER_COMM_FAILURE = 0x5,
> +    OCS_ABORTED = 0x6,
> +    OCS_FATAL_ERROR = 0x7,
> +    OCS_DEVICE_FATAL_ERROR = 0x8,
> +    OCS_INVALID_CRYPTO_CONFIG = 0x9,
> +    OCS_GENERAL_CRYPTO_ERROR = 0xa,
> +    OCS_INVALID_COMMAND_STATUS = 0xf,
> +};
> +
> +enum {
> +    MASK_OCS = 0x0F,
> +};
> +
> +/*
> + * struct UfshcdSgEntry - UFSHCI PRD Entry
> + * @addr: Physical address; DW-0 and DW-1.
> + * @reserved: Reserved for future use DW-2
> + * @size: size of physical segment DW-3
> + */
> +typedef struct QEMU_PACKED UfshcdSgEntry {
> +    __le64 addr;
> +    __le32 reserved;
> +    __le32 size;
> +    /*
> +     * followed by variant-specific fields if
> +     * CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE has been defined.
> +     */
> +} UfshcdSgEntry;
> +
> +/*
> + * struct RequestDescHeader - Descriptor Header common to both UTRD and UTMRD
> + * @dword0: Descriptor Header DW0
> + * @dword1: Descriptor Header DW1
> + * @dword2: Descriptor Header DW2
> + * @dword3: Descriptor Header DW3
> + */
> +typedef struct QEMU_PACKED RequestDescHeader {
> +    __le32 dword_0;
> +    __le32 dword_1;
> +    __le32 dword_2;
> +    __le32 dword_3;
> +} RequestDescHeader;

docs/devel/style.rst:Don't use Linux kernel internal types like u32, __u32 or __le32.

Please use uint32_t.

> +
> +/*
> + * struct UtpTransferReqDesc - UTP Transfer Request Descriptor (UTRD)
> + * @header: UTRD header DW-0 to DW-3
> + * @command_desc_base_addr_lo: UCD base address low DW-4
> + * @command_desc_base_addr_hi: UCD base address high DW-5
> + * @response_upiu_length: response UPIU length DW-6
> + * @response_upiu_offset: response UPIU offset DW-6
> + * @prd_table_length: Physical region descriptor length DW-7
> + * @prd_table_offset: Physical region descriptor offset DW-7
> + */
> +typedef struct QEMU_PACKED UtpTransferReqDesc {
> +    /* DW 0-3 */
> +    RequestDescHeader header;
> +
> +    /* DW 4-5*/
> +    __le32 command_desc_base_addr_lo;
> +    __le32 command_desc_base_addr_hi;
> +
> +    /* DW 6 */
> +    __le16 response_upiu_length;
> +    __le16 response_upiu_offset;
> +
> +    /* DW 7 */
> +    __le16 prd_table_length;
> +    __le16 prd_table_offset;
> +} UtpTransferReqDesc;
> +
> +/*
> + * The code below is copied from the linux kernel ("include/ufs/ufs.h") and
> + * modified to fit the qemu style.
> + */
> +
> +#define GENERAL_UPIU_REQUEST_SIZE (sizeof(UtpUpiuReq))
> +#define QUERY_DESC_MAX_SIZE 255
> +#define QUERY_DESC_MIN_SIZE 2
> +#define QUERY_DESC_HDR_SIZE 2
> +#define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - (sizeof(UtpUpiuHeader)))
> +#define UFS_SENSE_SIZE 18
> +
> +#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0) \
> +    cpu_to_be32((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | (byte0))
> +/*
> + * UFS device may have standard LUs and LUN id could be from 0x00 to
> + * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
> + * UFS device may also have the Well Known LUs (also referred as W-LU)
> + * which again could be from 0x00 to 0x7F. For W-LUs, device only use
> + * the "Extended Addressing Format" which means the W-LUNs would be
> + * from 0xc100 (SCSI_W_LUN_BASE) onwards.
> + * This means max. LUN number reported from UFS device could be 0xC17F.
> + */
> +#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F
> +#define UFS_UPIU_WLUN_ID (1 << 7)
> +
> +/* WriteBooster buffer is available only for the logical unit from 0 to 7 */
> +#define UFS_UPIU_MAX_WB_LUN_ID 8
> +
> +/*
> + * WriteBooster buffer lifetime has a limit setted by vendor.
> + * If it is over the limit, WriteBooster feature will be disabled.
> + */
> +#define UFS_WB_EXCEED_LIFETIME 0x0B
> +
> +/*
> + * In UFS Spec, the Extra Header Segment (EHS) starts from byte 32 in UPIU
> + * request/response packet
> + */
> +#define EHS_OFFSET_IN_RESPONSE 32
> +
> +/* Well known logical unit id in LUN field of UPIU */
> +enum {
> +    UFS_UPIU_REPORT_LUNS_WLUN = 0x81,
> +    UFS_UPIU_UFS_DEVICE_WLUN = 0xD0,
> +    UFS_UPIU_BOOT_WLUN = 0xB0,
> +    UFS_UPIU_RPMB_WLUN = 0xC4,
> +};
> +
> +/*
> + * UFS Protocol Information Unit related definitions
> + */
> +
> +/* Task management functions */
> +enum {
> +    UFS_ABORT_TASK = 0x01,
> +    UFS_ABORT_TASK_SET = 0x02,
> +    UFS_CLEAR_TASK_SET = 0x04,
> +    UFS_LOGICAL_RESET = 0x08,
> +    UFS_QUERY_TASK = 0x80,
> +    UFS_QUERY_TASK_SET = 0x81,
> +};
> +
> +/* UTP UPIU Transaction Codes Initiator to Target */
> +enum {
> +    UPIU_TRANSACTION_NOP_OUT = 0x00,
> +    UPIU_TRANSACTION_COMMAND = 0x01,
> +    UPIU_TRANSACTION_DATA_OUT = 0x02,
> +    UPIU_TRANSACTION_TASK_REQ = 0x04,
> +    UPIU_TRANSACTION_QUERY_REQ = 0x16,
> +};
> +
> +/* UTP UPIU Transaction Codes Target to Initiator */
> +enum {
> +    UPIU_TRANSACTION_NOP_IN = 0x20,
> +    UPIU_TRANSACTION_RESPONSE = 0x21,
> +    UPIU_TRANSACTION_DATA_IN = 0x22,
> +    UPIU_TRANSACTION_TASK_RSP = 0x24,
> +    UPIU_TRANSACTION_READY_XFER = 0x31,
> +    UPIU_TRANSACTION_QUERY_RSP = 0x36,
> +    UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
> +};
> +
> +/* UPIU Read/Write flags */
> +enum {
> +    UPIU_CMD_FLAGS_NONE = 0x00,
> +    UPIU_CMD_FLAGS_WRITE = 0x20,
> +    UPIU_CMD_FLAGS_READ = 0x40,
> +};
> +
> +/* UPIU Task Attributes */
> +enum {
> +    UPIU_TASK_ATTR_SIMPLE = 0x00,
> +    UPIU_TASK_ATTR_ORDERED = 0x01,
> +    UPIU_TASK_ATTR_HEADQ = 0x02,
> +    UPIU_TASK_ATTR_ACA = 0x03,
> +};
> +
> +/* UPIU Query request function */
> +enum {
> +    UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
> +    UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
> +};
> +
> +/* Flag idn for Query Requests*/
> +enum flag_idn {
> +    QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
> +    QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
> +    QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
> +    QUERY_FLAG_IDN_BKOPS_EN = 0x04,
> +    QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
> +    QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
> +    QUERY_FLAG_IDN_REFRESH_ENABLE = 0x07,
> +    QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
> +    QUERY_FLAG_IDN_BUSY_RTC = 0x09,
> +    QUERY_FLAG_IDN_RESERVED3 = 0x0A,
> +    QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
> +    QUERY_FLAG_IDN_WB_EN = 0x0E,
> +    QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN = 0x0F,
> +    QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8 = 0x10,
> +    QUERY_FLAG_IDN_HPB_RESET = 0x11,
> +    QUERY_FLAG_IDN_HPB_EN = 0x12,
> +    QUERY_FLAG_IDN_COUNT,
> +};
> +
> +/* Attribute idn for Query requests */
> +enum attr_idn {
> +    QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
> +    QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD = 0x01,
> +    QUERY_ATTR_IDN_POWER_MODE = 0x02,
> +    QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
> +    QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
> +    QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
> +    QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
> +    QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
> +    QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
> +    QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
> +    QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
> +    QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
> +    QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
> +    QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
> +    QUERY_ATTR_IDN_EE_STATUS = 0x0E,
> +    QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
> +    QUERY_ATTR_IDN_CNTX_CONF = 0x10,
> +    QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
> +    QUERY_ATTR_IDN_RESERVED2 = 0x12,
> +    QUERY_ATTR_IDN_RESERVED3 = 0x13,
> +    QUERY_ATTR_IDN_FFU_STATUS = 0x14,
> +    QUERY_ATTR_IDN_PSA_STATE = 0x15,
> +    QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
> +    QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME = 0x17,
> +    QUERY_ATTR_IDN_CASE_ROUGH_TEMP = 0x18,
> +    QUERY_ATTR_IDN_HIGH_TEMP_BOUND = 0x19,
> +    QUERY_ATTR_IDN_LOW_TEMP_BOUND = 0x1A,
> +    QUERY_ATTR_IDN_THROTTLING_STATUS = 0x1B,
> +    QUERY_ATTR_IDN_WB_FLUSH_STATUS = 0x1C,
> +    QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D,
> +    QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
> +    QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
> +    QUERY_ATTR_IDN_REFRESH_STATUS = 0x2C,
> +    QUERY_ATTR_IDN_REFRESH_FREQ = 0x2D,
> +    QUERY_ATTR_IDN_REFRESH_UNIT = 0x2E,
> +    QUERY_ATTR_IDN_COUNT,
> +};
> +
> +/* Descriptor idn for Query requests */
> +enum desc_idn {
> +    QUERY_DESC_IDN_DEVICE = 0x0,
> +    QUERY_DESC_IDN_CONFIGURATION = 0x1,
> +    QUERY_DESC_IDN_UNIT = 0x2,
> +    QUERY_DESC_IDN_RFU_0 = 0x3,
> +    QUERY_DESC_IDN_INTERCONNECT = 0x4,
> +    QUERY_DESC_IDN_STRING = 0x5,
> +    QUERY_DESC_IDN_RFU_1 = 0x6,
> +    QUERY_DESC_IDN_GEOMETRY = 0x7,
> +    QUERY_DESC_IDN_POWER = 0x8,
> +    QUERY_DESC_IDN_HEALTH = 0x9,
> +    QUERY_DESC_IDN_MAX,
> +};
> +
> +enum desc_header_offset {
> +    QUERY_DESC_LENGTH_OFFSET = 0x00,
> +    QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
> +};
> +
> +/* Unit descriptor parameters offsets in bytes*/
> +enum unit_desc_param {
> +    UNIT_DESC_PARAM_LEN = 0x0,
> +    UNIT_DESC_PARAM_TYPE = 0x1,
> +    UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
> +    UNIT_DESC_PARAM_LU_ENABLE = 0x3,
> +    UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
> +    UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
> +    UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
> +    UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
> +    UNIT_DESC_PARAM_MEM_TYPE = 0x8,
> +    UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9,
> +    UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
> +    UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
> +    UNIT_DESC_PARAM_ERASE_BLK_SIZE = 0x13,
> +    UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
> +    UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
> +    UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20,
> +    UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
> +    UNIT_DESC_PARAM_HPB_LU_MAX_ACTIVE_RGNS = 0x23,
> +    UNIT_DESC_PARAM_HPB_PIN_RGN_START_OFF = 0x25,
> +    UNIT_DESC_PARAM_HPB_NUM_PIN_RGNS = 0x27,
> +    UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29,
> +};
> +
> +/* RPMB Unit descriptor parameters offsets in bytes*/
> +enum rpmb_unit_desc_param {
> +    RPMB_UNIT_DESC_PARAM_LEN = 0x0,
> +    RPMB_UNIT_DESC_PARAM_TYPE = 0x1,
> +    RPMB_UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
> +    RPMB_UNIT_DESC_PARAM_LU_ENABLE = 0x3,
> +    RPMB_UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
> +    RPMB_UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
> +    RPMB_UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
> +    RPMB_UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
> +    RPMB_UNIT_DESC_PARAM_MEM_TYPE = 0x8,
> +    RPMB_UNIT_DESC_PARAM_REGION_EN = 0x9,
> +    RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
> +    RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
> +    RPMB_UNIT_DESC_PARAM_REGION0_SIZE = 0x13,
> +    RPMB_UNIT_DESC_PARAM_REGION1_SIZE = 0x14,
> +    RPMB_UNIT_DESC_PARAM_REGION2_SIZE = 0x15,
> +    RPMB_UNIT_DESC_PARAM_REGION3_SIZE = 0x16,
> +    RPMB_UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
> +    RPMB_UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
> +};
> +
> +/* Device descriptor parameters offsets in bytes*/
> +enum device_desc_param {
> +    DEVICE_DESC_PARAM_LEN = 0x0,
> +    DEVICE_DESC_PARAM_TYPE = 0x1,
> +    DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2,
> +    DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3,
> +    DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4,
> +    DEVICE_DESC_PARAM_PRTCL = 0x5,
> +    DEVICE_DESC_PARAM_NUM_LU = 0x6,
> +    DEVICE_DESC_PARAM_NUM_WLU = 0x7,
> +    DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
> +    DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9,
> +    DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
> +    DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB,
> +    DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC,
> +    DEVICE_DESC_PARAM_SEC_LU = 0xD,
> +    DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE,
> +    DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
> +    DEVICE_DESC_PARAM_SPEC_VER = 0x10,
> +    DEVICE_DESC_PARAM_MANF_DATE = 0x12,
> +    DEVICE_DESC_PARAM_MANF_NAME = 0x14,
> +    DEVICE_DESC_PARAM_PRDCT_NAME = 0x15,
> +    DEVICE_DESC_PARAM_SN = 0x16,
> +    DEVICE_DESC_PARAM_OEM_ID = 0x17,
> +    DEVICE_DESC_PARAM_MANF_ID = 0x18,
> +    DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
> +    DEVICE_DESC_PARAM_UD_LEN = 0x1B,
> +    DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
> +    DEVICE_DESC_PARAM_FRQ_RTC = 0x1D,
> +    DEVICE_DESC_PARAM_UFS_FEAT = 0x1F,
> +    DEVICE_DESC_PARAM_FFU_TMT = 0x20,
> +    DEVICE_DESC_PARAM_Q_DPTH = 0x21,
> +    DEVICE_DESC_PARAM_DEV_VER = 0x22,
> +    DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24,
> +    DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25,
> +    DEVICE_DESC_PARAM_PSA_TMT = 0x29,
> +    DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
> +    DEVICE_DESC_PARAM_HPB_VER = 0x40,
> +    DEVICE_DESC_PARAM_HPB_CONTROL = 0x42,
> +    DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
> +    DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53,
> +    DEVICE_DESC_PARAM_WB_TYPE = 0x54,
> +    DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55,
> +};
> +
> +/* Interconnect descriptor parameters offsets in bytes*/
> +enum interconnect_desc_param {
> +    INTERCONNECT_DESC_PARAM_LEN = 0x0,
> +    INTERCONNECT_DESC_PARAM_TYPE = 0x1,
> +    INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2,
> +    INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4,
> +};
> +
> +/* Geometry descriptor parameters offsets in bytes*/
> +enum geometry_desc_param {
> +    GEOMETRY_DESC_PARAM_LEN = 0x0,
> +    GEOMETRY_DESC_PARAM_TYPE = 0x1,
> +    GEOMETRY_DESC_PARAM_DEV_CAP = 0x4,
> +    GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC,
> +    GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD,
> +    GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11,
> +    GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12,
> +    GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13,
> +    GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14,
> +    GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15,
> +    GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16,
> +    GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17,
> +    GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18,
> +    GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19,
> +    GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A,
> +    GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B,
> +    GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C,
> +    GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D,
> +    GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E,
> +    GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20,
> +    GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24,
> +    GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26,
> +    GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A,
> +    GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C,
> +    GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30,
> +    GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32,
> +    GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36,
> +    GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38,
> +    GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C,
> +    GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E,
> +    GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42,
> +    GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44,
> +    GEOMETRY_DESC_PARAM_HPB_REGION_SIZE = 0x48,
> +    GEOMETRY_DESC_PARAM_HPB_NUMBER_LU = 0x49,
> +    GEOMETRY_DESC_PARAM_HPB_SUBREGION_SIZE = 0x4A,
> +    GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS = 0x4B,
> +    GEOMETRY_DESC_PARAM_WB_MAX_ALLOC_UNITS = 0x4F,
> +    GEOMETRY_DESC_PARAM_WB_MAX_WB_LUNS = 0x53,
> +    GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54,
> +    GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55,
> +    GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56,
> +};
> +
> +/* Health descriptor parameters offsets in bytes*/
> +enum health_desc_param {
> +    HEALTH_DESC_PARAM_LEN = 0x0,
> +    HEALTH_DESC_PARAM_TYPE = 0x1,
> +    HEALTH_DESC_PARAM_EOL_INFO = 0x2,
> +    HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3,
> +    HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
> +};
> +
> +/* WriteBooster buffer mode */
> +enum {
> +    WB_BUF_MODE_LU_DEDICATED = 0x0,
> +    WB_BUF_MODE_SHARED = 0x1,
> +};
> +
> +/*
> + * Logical Unit Write Protect
> + * 00h: LU not write protected
> + * 01h: LU write protected when fPowerOnWPEn =1
> + * 02h: LU permanently write protected when fPermanentWPEn =1
> + */
> +enum ufs_lu_wp_type {
> +    UFS_LU_NO_WP = 0x00,
> +    UFS_LU_POWER_ON_WP = 0x01,
> +    UFS_LU_PERM_WP = 0x02,
> +};
> +
> +/* UTP QUERY Transaction Specific Fields OpCode */
> +enum query_opcode {
> +    UPIU_QUERY_OPCODE_NOP = 0x0,
> +    UPIU_QUERY_OPCODE_READ_DESC = 0x1,
> +    UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
> +    UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
> +    UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
> +    UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
> +    UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
> +    UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
> +    UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
> +};
> +
> +/* Query response result code */
> +typedef enum QueryRespCode {
> +    QUERY_RESULT_SUCCESS = 0x00,
> +    QUERY_RESULT_NOT_READABLE = 0xF6,
> +    QUERY_RESULT_NOT_WRITEABLE = 0xF7,
> +    QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
> +    QUERY_RESULT_INVALID_LENGTH = 0xF9,
> +    QUERY_RESULT_INVALID_VALUE = 0xFA,
> +    QUERY_RESULT_INVALID_SELECTOR = 0xFB,
> +    QUERY_RESULT_INVALID_INDEX = 0xFC,
> +    QUERY_RESULT_INVALID_IDN = 0xFD,
> +    QUERY_RESULT_INVALID_OPCODE = 0xFE,
> +    QUERY_RESULT_GENERAL_FAILURE = 0xFF,
> +} QueryRespCode;
> +
> +/* UTP Transfer Request Command Type (CT) */
> +enum {
> +    UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
> +    UPIU_COMMAND_SET_TYPE_UFS = 0x1,
> +    UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
> +};
> +
> +/* Task management service response */
> +enum {
> +    UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00,
> +    UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
> +    UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08,
> +    UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
> +    UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
> +};
> +
> +/* UFS device power modes */
> +enum ufs_dev_pwr_mode {
> +    UFS_ACTIVE_PWR_MODE = 1,
> +    UFS_SLEEP_PWR_MODE = 2,
> +    UFS_POWERDOWN_PWR_MODE = 3,
> +    UFS_DEEPSLEEP_PWR_MODE = 4,
> +};
> +
> +/*
> + * struct UtpCmdRsp - Response UPIU structure
> + * @residual_transfer_count: Residual transfer count DW-3
> + * @reserved: Reserved double words DW-4 to DW-7
> + * @sense_data_len: Sense data length DW-8 U16
> + * @sense_data: Sense data field DW-8 to DW-12
> + */
> +typedef struct QEMU_PACKED UtpCmdRsp {
> +    uint32_t residual_transfer_count;
> +    uint32_t reserved[4];
> +    uint16_t sense_data_len;
> +    uint8_t sense_data[UFS_SENSE_SIZE];
> +} UtpCmdRsp;
> +
> +/*
> + * struct UtpUpiuRsp - general upiu response structure
> + * @header: UPIU header structure DW-0 to DW-2
> + * @sr: fields structure for scsi command DW-3 to DW-12
> + * @qr: fields structure for query request DW-3 to DW-7
> + */
> +typedef struct QEMU_PACKED UtpUpiuRsp {
> +    UtpUpiuHeader header;
> +    union {
> +        UtpCmdRsp sr;
> +        UtpUpiuQuery qr;
> +    };
> +} UtpUpiuRsp;
> +
> +static inline void _ufs_check_size(void)
> +{
> +    QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104);
> +    QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89);
> +    QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87);
> +    QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45);
> +    QEMU_BUILD_BUG_ON(sizeof(RpmbUnitDescriptor) != 35);
> +    QEMU_BUILD_BUG_ON(sizeof(PowerParametersDescriptor) != 98);
> +    QEMU_BUILD_BUG_ON(sizeof(InterconnectDescriptor) != 6);
> +    QEMU_BUILD_BUG_ON(sizeof(StringDescriptor) != 254);
> +    QEMU_BUILD_BUG_ON(sizeof(DeviceHealthDescriptor) != 45);
> +    QEMU_BUILD_BUG_ON(sizeof(Flags) != 0x13);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuHeader) != 12);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuQuery) != 276);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuCmd) != 20);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuReq) != 288);
> +    QEMU_BUILD_BUG_ON(sizeof(UfshcdSgEntry) != 16);
> +    QEMU_BUILD_BUG_ON(sizeof(RequestDescHeader) != 16);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpTransferReqDesc) != 32);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40);
> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288);
> +}
> +#endif
> diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
> index e6d0574a29..1094274546 100644
> --- a/include/hw/pci/pci.h
> +++ b/include/hw/pci/pci.h
> @@ -114,6 +114,7 @@ extern bool pci_available;
>  #define PCI_DEVICE_ID_REDHAT_NVME        0x0010
>  #define PCI_DEVICE_ID_REDHAT_PVPANIC     0x0011
>  #define PCI_DEVICE_ID_REDHAT_ACPI_ERST   0x0012
> +#define PCI_DEVICE_ID_REDHAT_UFS         0x0013

Please update docs/specs/pci-ids.rst in order to reserve this device ID.

>  #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
>  
>  #define FMT_PCIBUS                      PRIx64
> diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
> index e4386ebb20..85469b9b53 100644
> --- a/include/hw/pci/pci_ids.h
> +++ b/include/hw/pci/pci_ids.h
> @@ -26,6 +26,7 @@
>  #define PCI_CLASS_STORAGE_SATA           0x0106
>  #define PCI_CLASS_STORAGE_SAS            0x0107
>  #define PCI_CLASS_STORAGE_EXPRESS        0x0108
> +#define PCI_CLASS_STORAGE_UFS            0x0109
>  #define PCI_CLASS_STORAGE_OTHER          0x0180
>  
>  #define PCI_BASE_CLASS_NETWORK           0x02
> diff --git a/meson.build b/meson.build
> index 34306a6205..6aeaa155db 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -3268,6 +3268,7 @@ if have_system
>      'hw/ssi',
>      'hw/timer',
>      'hw/tpm',
> +    'hw/ufs',
>      'hw/usb',
>      'hw/vfio',
>      'hw/virtio',
> -- 
> 2.34.1
>
Jeuk Kim June 19, 2023, 10:04 a.m. UTC | #2
On Fri, Jun 16, 2023, Stefan Hajnoczi wrote:
>On Fri, Jun 16, 2023 at 03:58:21PM +0900, Jeuk Kim wrote:
>> Universal Flash Storage (UFS) is a high-performance mass storage device
>> with a serial interface. It is primarily used as a high-performance
>> data storage device for embedded applications.
>> 
>> This commit contains code for UFS device to be recognized
>> as a UFS PCI device.
>> Patches to handle UFS logical unit and Transfer Request will follow.
>> 
>> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
>> ---
>>  MAINTAINERS              |    6 +
>>  hw/Kconfig               |    1 +
>>  hw/meson.build           |    1 +
>>  hw/ufs/Kconfig           |    4 +
>>  hw/ufs/meson.build       |    1 +
>>  hw/ufs/trace-events      |   33 ++
>>  hw/ufs/trace.h           |    1 +
>>  hw/ufs/ufs.c             |  305 +++++++++++
>>  hw/ufs/ufs.h             |   42 ++
>>  include/block/ufs.h      | 1048 ++++++++++++++++++++++++++++++++++++++
>>  include/hw/pci/pci.h     |    1 +
>>  include/hw/pci/pci_ids.h |    1 +
>>  meson.build              |    1 +
>>  13 files changed, 1445 insertions(+)
>>  create mode 100644 hw/ufs/Kconfig
>>  create mode 100644 hw/ufs/meson.build
>>  create mode 100644 hw/ufs/trace-events
>>  create mode 100644 hw/ufs/trace.h
>>  create mode 100644 hw/ufs/ufs.c
>>  create mode 100644 hw/ufs/ufs.h
>>  create mode 100644 include/block/ufs.h
>> 
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 88b5a7ee0a..91c2bfbb09 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -2231,6 +2231,12 @@ F: tests/qtest/nvme-test.c
>>  F: docs/system/devices/nvme.rst
>>  T: git git://git.infradead.org/qemu-nvme.git nvme-next
>>  
>> +ufs
>> +M: Jeuk Kim <jeuk20.kim@samsung.com>
>> +S: Supported
>> +F: hw/ufs/*
>> +F: include/block/ufs.h
>
>Thank you for stepping up as maintainer for UFS. The responsibilities of
>maintainers are to:
>
>1. Review other people's patches that modify the code.
>2. Send pull requests to the qemu.git maintainer or post your
>   acknowledgement of patches so another maintainer can merge patches
>   into a larger subsystem branch. For example, you could ack patches
>   and Klaus could include them in his pull requests for the time being.
>3. Ensure a basic level of testing and CI integration to prevent
>   bitrot and regressions.
>
>For this last point, I suggest writing a libqos test that performs
>device initialization and executes some basic I/O. Not all existing
>emulated storage controllers have this level of testing, but I highly
>recommend having an automated test case for your new device. There is
>documentation on how to write tests here:
>https://qemu.readthedocs.io/en/latest/devel/qtest.html
>
>An example test case is the AHCI (SATA) controller test:
>https://gitlab.com/qemu-project/qemu/-/blob/master/tests/qtest/ahci-test.c
>

I'll keep the maintainer's responsibilities in mind.
I'll prepare the libqos test in the next patch.
Thank you!!

>>  megasas
>>  M: Hannes Reinecke <hare@suse.com>
>>  L: qemu-block@nongnu.org
>> diff --git a/hw/Kconfig b/hw/Kconfig
>> index ba62ff6417..9ca7b38c31 100644
>> --- a/hw/Kconfig
>> +++ b/hw/Kconfig
>> @@ -38,6 +38,7 @@ source smbios/Kconfig
>>  source ssi/Kconfig
>>  source timer/Kconfig
>>  source tpm/Kconfig
>> +source ufs/Kconfig
>>  source usb/Kconfig
>>  source virtio/Kconfig
>>  source vfio/Kconfig
>> diff --git a/hw/meson.build b/hw/meson.build
>> index c7ac7d3d75..f01fac4617 100644
>> --- a/hw/meson.build
>> +++ b/hw/meson.build
>> @@ -37,6 +37,7 @@ subdir('smbios')
>>  subdir('ssi')
>>  subdir('timer')
>>  subdir('tpm')
>> +subdir('ufs')
>>  subdir('usb')
>>  subdir('vfio')
>>  subdir('virtio')
>> diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig
>> new file mode 100644
>> index 0000000000..b7b3392e85
>> --- /dev/null
>> +++ b/hw/ufs/Kconfig
>> @@ -0,0 +1,4 @@
>> +config UFS_PCI
>> +    bool
>> +    default y if PCI_DEVICES
>> +    depends on PCI
>> diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build
>> new file mode 100644
>> index 0000000000..c1d90eeea6
>> --- /dev/null
>> +++ b/hw/ufs/meson.build
>> @@ -0,0 +1 @@
>> +softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c'))
>> diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
>> new file mode 100644
>> index 0000000000..17793929b1
>> --- /dev/null
>> +++ b/hw/ufs/trace-events
>> @@ -0,0 +1,33 @@
>> +# ufs.c
>> +ufs_irq_raise(void) "INTx"
>> +ufs_irq_lower(void) "INTx"
>> +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
>> +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
>> +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32""
>> +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
>> +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
>> +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
>> +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32""
>> +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8""
>> +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8""
>> +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32""
>> +
>> +# error condition
>> +ufs_err_memory_allocation(void) "failed to allocate memory"
>> +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
>> +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""
>> +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
>> +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
>> +ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64""
>> +ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy"
>> +ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported"
>> +ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid"
>> +ufs_err_scsi_cmd_invalid_lun(uint8_t lun) "scsi command has invalid lun: 0x%"PRIx8""
>> +ufs_err_query_flag_not_readable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to read"
>> +ufs_err_query_flag_not_writable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to write"
>> +ufs_err_query_attr_not_readable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to read"
>> +ufs_err_query_attr_not_writable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to write"
>> +ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. opcode: 0x%"PRIx8""
>> +ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8""
>> +ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8""
>> +ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8""
>> diff --git a/hw/ufs/trace.h b/hw/ufs/trace.h
>> new file mode 100644
>> index 0000000000..2dbd6397c3
>> --- /dev/null
>> +++ b/hw/ufs/trace.h
>> @@ -0,0 +1 @@
>> +#include "trace/trace-hw_ufs.h"
>> diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
>> new file mode 100644
>> index 0000000000..9dba1073a8
>> --- /dev/null
>> +++ b/hw/ufs/ufs.c
>> @@ -0,0 +1,305 @@
>> +/*
>> + * QEMU Universal Flash Storage (UFS) Controller
>> + *
>> + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
>> + *
>> + * Written by Jeuk Kim <jeuk20.kim@samsung.com>
>> + *
>> + * This code is licensed under the GNU GPL v2 or later.
>
>Please use SPDX license identifiers in new files to make it easier for
>license tooling:
>
> * SPDX-License-Identifier: GPL-2.0-or-later
>

Okay, I'll fix it.

>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "migration/vmstate.h"
>> +#include "trace.h"
>> +#include "ufs.h"
>> +
>> +/* The QEMU-UFS device follows spec version 3.1 */
>> +#define UFS_SPEC_VER 0x00000310
>> +#define UFS_MAX_NUTRS 32
>> +#define UFS_MAX_NUTMRS 8
>> +
>> +static void ufs_irq_check(UfsHc *u)
>> +{
>> +    PCIDevice *pci = PCI_DEVICE(u);
>> +    uint32_t is = ldl_le_p(&u->reg.is);
>> +    uint32_t ie = ldl_le_p(&u->reg.ie);
>> +
>> +    if ((is & UFS_INTR_MASK) & ie) {
>> +        trace_ufs_irq_raise();
>> +        pci_irq_assert(pci);
>> +    } else {
>> +        trace_ufs_irq_lower();
>> +        pci_irq_deassert(pci);
>> +    }
>> +}
>
>Old-school PCI interrupts! I'm surprised there are no MSI-X interrupts.
>:)
>

I couldn't find anything MSI-X related in the ufs spec.
I guess this is because UFS is primarily designed to be platform based rather than PCI :(

>> +
>> +static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
>> +{
>> +    uint32_t is = ldl_le_p(&u->reg.is);
>> +    uint32_t hcs = ldl_le_p(&u->reg.hcs);
>> +    uint32_t ucmdarg1 = ldl_le_p(&u->reg.ucmdarg1);
>> +    uint32_t ucmdarg2 = ldl_le_p(&u->reg.ucmdarg2);
>> +    uint32_t ucmdarg3 = ldl_le_p(&u->reg.ucmdarg3);
>> +
>> +    trace_ufs_process_uiccmd(val, ucmdarg1, ucmdarg2, ucmdarg3);
>> +    /*
>> +     * Only the essential uic commands for running drivers on Linux and Windows
>> +     * are implemented.
>> +     */
>> +    switch (val) {
>> +    case UIC_CMD_DME_LINK_STARTUP:
>> +        hcs = FIELD_DP32(hcs, HCS, DP, 1);
>> +        hcs = FIELD_DP32(hcs, HCS, UTRLRDY, 1);
>> +        hcs = FIELD_DP32(hcs, HCS, UTMRLRDY, 1);
>> +        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
>> +        break;
>> +    /* TODO: Revisit it when Power Management is implemented */
>> +    case UIC_CMD_DME_HIBER_ENTER:
>> +        is = FIELD_DP32(is, IS, UHES, 1);
>> +        hcs = FIELD_DP32(hcs, HCS, UPMCRS, PWR_LOCAL);
>> +        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
>> +        break;
>> +    case UIC_CMD_DME_HIBER_EXIT:
>> +        is = FIELD_DP32(is, IS, UHXS, 1);
>> +        hcs = FIELD_DP32(hcs, HCS, UPMCRS, PWR_LOCAL);
>> +        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
>> +        break;
>> +    default:
>> +        ucmdarg2 = UIC_CMD_RESULT_FAILURE;
>> +    }
>> +
>> +    is = FIELD_DP32(is, IS, UCCS, 1);
>> +
>> +    stl_le_p(&u->reg.is, is);
>> +    stl_le_p(&u->reg.hcs, hcs);
>> +    stl_le_p(&u->reg.ucmdarg1, ucmdarg1);
>> +    stl_le_p(&u->reg.ucmdarg2, ucmdarg2);
>> +    stl_le_p(&u->reg.ucmdarg3, ucmdarg3);
>> +
>> +    ufs_irq_check(u);
>> +}
>> +
>> +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 utrlcnr = ldl_le_p(&u->reg.utrlcnr);
>> +    uint32_t utrlba, utmrlba;
>> +
>> +    switch (offset) {
>> +    case A_IS:
>> +        is &= ~data;
>> +        stl_le_p(&u->reg.is, is);
>> +        ufs_irq_check(u);
>> +        break;
>> +    case A_IE:
>> +        stl_le_p(&u->reg.ie, data);
>> +        ufs_irq_check(u);
>> +        break;
>> +    case A_HCE:
>> +        if (!FIELD_EX32(hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) {
>> +            hcs = FIELD_DP32(hcs, HCS, UCRDY, 1);
>> +            hce = FIELD_DP32(hce, HCE, HCE, 1);
>> +            stl_le_p(&u->reg.hcs, hcs);
>> +            stl_le_p(&u->reg.hce, hce);
>> +        } else if (FIELD_EX32(hce, HCE, HCE) && !FIELD_EX32(data, HCE, HCE)) {
>> +            hcs = 0;
>> +            hce = FIELD_DP32(hce, HCE, HCE, 0);
>> +            stl_le_p(&u->reg.hcs, hcs);
>> +            stl_le_p(&u->reg.hce, hce);
>> +        }
>> +        break;
>> +    case A_UTRLBA:
>> +        utrlba = data & R_UTRLBA_UTRLBA_MASK;
>> +        stl_le_p(&u->reg.utrlba, utrlba);
>> +        break;
>> +    case A_UTRLBAU:
>> +        stl_le_p(&u->reg.utrlbau, data);
>> +        break;
>> +    case A_UTRLDBR:
>> +        /* Not yet supported */
>> +        break;
>> +    case A_UTRLRSR:
>> +        stl_le_p(&u->reg.utrlrsr, data);
>> +        break;
>> +    case A_UTRLCNR:
>> +        utrlcnr &= ~data;
>> +        stl_le_p(&u->reg.utrlcnr, utrlcnr);
>> +        break;
>> +    case A_UTMRLBA:
>> +        utmrlba = data & R_UTMRLBA_UTMRLBA_MASK;
>> +        stl_le_p(&u->reg.utmrlba, utmrlba);
>> +        break;
>> +    case A_UTMRLBAU:
>> +        stl_le_p(&u->reg.utmrlbau, data);
>> +        break;
>> +    case A_UICCMD:
>> +        ufs_process_uiccmd(u, data);
>> +        break;
>> +    case A_UCMDARG1:
>> +        stl_le_p(&u->reg.ucmdarg1, data);
>> +        break;
>> +    case A_UCMDARG2:
>> +        stl_le_p(&u->reg.ucmdarg2, data);
>> +        break;
>> +    case A_UCMDARG3:
>> +        stl_le_p(&u->reg.ucmdarg3, data);
>> +        break;
>> +    case A_UTRLCLR:
>> +    case A_UTMRLDBR:
>> +    case A_UTMRLCLR:
>> +    case A_UTMRLRSR:
>> +        trace_ufs_err_unsupport_register_offset(offset);
>> +        break;
>> +    default:
>> +        trace_ufs_err_invalid_register_offset(offset);
>> +        break;
>> +    }
>> +}
>> +
>> +static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    UfsHc *u = (UfsHc *)opaque;
>> +    uint8_t *ptr = (uint8_t *)&u->reg;
>> +    uint64_t value;
>> +
>> +    if (addr > sizeof(u->reg) - size) {
>> +        trace_ufs_err_invalid_register_offset(addr);
>> +        return 0;
>> +    }
>> +
>> +    value = ldn_le_p(ptr + addr, size);
>> +    trace_ufs_mmio_read(addr, value, size);
>> +    return value;
>> +}
>> +
>> +static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
>> +                           unsigned size)
>> +{
>> +    UfsHc *u = (UfsHc *)opaque;
>> +
>> +    if (addr > sizeof(u->reg) - size) {
>> +        trace_ufs_err_invalid_register_offset(addr);
>> +        return;
>> +    }
>> +
>> +    trace_ufs_mmio_write(addr, data, size);
>> +    ufs_write_reg(u, addr, data, size);
>> +}
>> +
>> +static const MemoryRegionOps ufs_mmio_ops = {
>> +    .read = ufs_mmio_read,
>> +    .write = ufs_mmio_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .impl = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    },
>> +};
>> +
>> +static bool ufs_check_constraints(UfsHc *u, Error **errp)
>> +{
>> +    if (u->params.nutrs > UFS_MAX_NUTRS) {
>> +        error_setg(errp, "nutrs must be less than %d", UFS_MAX_NUTRS);
>
>Off-by-one: the if statement condition checks for <= but the error
>message says <.
>

Thanks, I'll fix it.

>> +        return false;
>> +    }
>> +
>> +    if (u->params.nutmrs > UFS_MAX_NUTMRS) {
>> +        error_setg(errp, "nutmrs must be less than %d", UFS_MAX_NUTMRS);
>
>Same here.
>

Thanks, I'll fix it.

>> +        return false;
>> +    }
>> +
>> +    return true;
>> +}
>> +
>> +static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
>> +{
>> +    uint8_t *pci_conf = pci_dev->config;
>> +
>> +    pci_conf[PCI_INTERRUPT_PIN] = 1;
>> +    pci_config_set_prog_interface(pci_conf, 0x1);
>> +
>> +    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT);
>> +    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REDHAT_UFS);
>> +
>> +    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_UFS);
>> +
>> +    memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
>> +                          u->reg_size);
>> +    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
>> +    u->irq = pci_allocate_irq(pci_dev);
>> +}
>> +
>> +static void ufs_init_hc(UfsHc *u)
>> +{
>> +    uint32_t cap = 0;
>> +
>> +    u->reg_size = pow2ceil(sizeof(UfsReg));
>> +
>> +    memset(&u->reg, 0, sizeof(u->reg));
>> +    cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
>> +    cap = FIELD_DP32(cap, CAP, RTT, 2);
>> +    cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
>> +    cap = FIELD_DP32(cap, CAP, AUTOH8, 0);
>> +    cap = FIELD_DP32(cap, CAP, 64AS, 1);
>> +    cap = FIELD_DP32(cap, CAP, OODDS, 0);
>> +    cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
>> +    cap = FIELD_DP32(cap, CAP, CS, 0);
>> +    stl_le_p(&u->reg.cap, cap);
>> +    stl_le_p(&u->reg.ver, UFS_SPEC_VER);
>> +}
>> +
>> +static void ufs_realize(PCIDevice *pci_dev, Error **errp)
>> +{
>> +    UfsHc *u = UFS(pci_dev);
>> +
>> +    if (!ufs_check_constraints(u, errp)) {
>> +        return;
>> +    }
>> +
>> +    ufs_init_hc(u);
>> +    ufs_init_pci(u, pci_dev);
>> +}
>> +
>> +static Property ufs_props[] = {
>> +    DEFINE_PROP_STRING("serial", UfsHc, params.serial),
>> +    DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
>> +    DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static const VMStateDescription ufs_vmstate = {
>> +    .name = "ufs",
>> +    .unmigratable = 1,
>> +};
>> +
>> +static void ufs_class_init(ObjectClass *oc, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(oc);
>> +    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
>> +
>> +    pc->realize = ufs_realize;
>> +    pc->class_id = PCI_CLASS_STORAGE_UFS;
>
>Why does ufs_init_pci() also call pci_config_set_class(pci_conf,
>PCI_CLASS_STORAGE_UFS)? The following values can be removed from
>ufs_init_pci() and initialized here instead:
>
>  pc->vendor_id = PCI_VENDOR_ID_REDHAT;
>  pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
>  pc->class_id = PCI_CLASS_STORAGE_UFS;
>

It was my mistake.
I'll fix it. Thanks!

>> +
>> +    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
>> +    dc->desc = "Universal Flash Storage";
>> +    device_class_set_props(dc, ufs_props);
>> +    dc->vmsd = &ufs_vmstate;
>> +}
>> +
>> +static const TypeInfo ufs_info = {
>> +    .name = TYPE_UFS,
>> +    .parent = TYPE_PCI_DEVICE,
>> +    .class_init = ufs_class_init,
>> +    .instance_size = sizeof(UfsHc),
>> +    .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
>> +};
>> +
>> +static void ufs_register_types(void)
>> +{
>> +    type_register_static(&ufs_info);
>> +}
>> +
>> +type_init(ufs_register_types)
>> diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
>> new file mode 100644
>> index 0000000000..3c28f4e62d
>> --- /dev/null
>> +++ b/hw/ufs/ufs.h
>> @@ -0,0 +1,42 @@
>> +/*
>> + * QEMU UFS
>> + *
>> + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
>> + *
>> + * Written by Jeuk Kim <jeuk20.kim@samsung.com>
>> + *
>> + * This code is licensed under the GNU GPL v2 or later.
>> + */
>> +
>> +#ifndef HW_UFS_UFS_H
>> +#define HW_UFS_UFS_H
>> +
>> +#include "hw/pci/pci_device.h"
>> +#include "hw/scsi/scsi.h"
>> +#include "block/ufs.h"
>> +
>> +#define UFS_MAX_LUS 32
>> +#define UFS_LOGICAL_BLK_SIZE 4096
>> +
>> +typedef struct UfsParams {
>> +    char *serial;
>> +    uint8_t nutrs; /* Number of UTP Transfer Request Slots */
>> +    uint8_t nutmrs; /* Number of UTP Task Management Request Slots */
>> +} UfsParams;
>> +
>> +typedef struct UfsHc {
>> +    PCIDevice parent_obj;
>> +    MemoryRegion iomem;
>> +    UfsReg reg;
>> +    UfsParams params;
>> +    uint32_t reg_size;
>> +
>> +    qemu_irq irq;
>> +    QEMUBH *doorbell_bh;
>> +    QEMUBH *complete_bh;
>> +} UfsHc;
>> +
>> +#define TYPE_UFS "ufs"
>> +#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
>> +
>> +#endif /* HW_UFS_UFS_H */
>> diff --git a/include/block/ufs.h b/include/block/ufs.h
>> new file mode 100644
>> index 0000000000..0e4c0e5d93
>> --- /dev/null
>> +++ b/include/block/ufs.h
>> @@ -0,0 +1,1048 @@
>> +#ifndef BLOCK_UFS_H
>> +#define BLOCK_UFS_H
>> +
>> +#include "hw/registerfields.h"
>> +
>> +typedef struct QEMU_PACKED UfsReg {
>> +    uint32_t cap;
>> +    uint32_t rsvd0;
>> +    uint32_t ver;
>> +    uint32_t rsvd1;
>> +    uint32_t hcpid;
>> +    uint32_t hcmid;
>> +    uint32_t ahit;
>> +    uint32_t rsvd2;
>> +    uint32_t is;
>> +    uint32_t ie;
>> +    uint32_t rsvd3[2];
>> +    uint32_t hcs;
>> +    uint32_t hce;
>> +    uint32_t uecpa;
>> +    uint32_t uecdl;
>> +    uint32_t uecn;
>> +    uint32_t uect;
>> +    uint32_t uecdme;
>> +    uint32_t utriacr;
>> +    uint32_t utrlba;
>> +    uint32_t utrlbau;
>> +    uint32_t utrldbr;
>> +    uint32_t utrlclr;
>> +    uint32_t utrlrsr;
>> +    uint32_t utrlcnr;
>> +    uint32_t rsvd4[2];
>> +    uint32_t utmrlba;
>> +    uint32_t utmrlbau;
>> +    uint32_t utmrldbr;
>> +    uint32_t utmrlclr;
>> +    uint32_t utmrlrsr;
>> +    uint32_t rsvd5[3];
>> +    uint32_t uiccmd;
>> +    uint32_t ucmdarg1;
>> +    uint32_t ucmdarg2;
>> +    uint32_t ucmdarg3;
>> +    uint32_t rsvd6[4];
>> +    uint32_t rsvd7[4];
>> +    uint32_t rsvd8[16];
>> +    uint32_t ccap;
>> +} UfsReg;
>> +
>> +REG32(CAP, offsetof(UfsReg, cap))
>> +    FIELD(CAP, NUTRS, 0, 5)
>> +    FIELD(CAP, RTT, 8, 8)
>> +    FIELD(CAP, NUTMRS, 16, 3)
>> +    FIELD(CAP, AUTOH8, 23, 1)
>> +    FIELD(CAP, 64AS, 24, 1)
>> +    FIELD(CAP, OODDS, 25, 1)
>> +    FIELD(CAP, UICDMETMS, 26, 1)
>> +    FIELD(CAP, CS, 28, 1)
>> +REG32(VER, offsetof(UfsReg, ver))
>> +REG32(HCPID, offsetof(UfsReg, hcpid))
>> +REG32(HCMID, offsetof(UfsReg, hcmid))
>> +REG32(AHIT, offsetof(UfsReg, ahit))
>> +REG32(IS, offsetof(UfsReg, is))
>> +    FIELD(IS, UTRCS, 0, 1)
>> +    FIELD(IS, UDEPRI, 1, 1)
>> +    FIELD(IS, UE, 2, 1)
>> +    FIELD(IS, UTMS, 3, 1)
>> +    FIELD(IS, UPMS, 4, 1)
>> +    FIELD(IS, UHXS, 5, 1)
>> +    FIELD(IS, UHES, 6, 1)
>> +    FIELD(IS, ULLS, 7, 1)
>> +    FIELD(IS, ULSS, 8, 1)
>> +    FIELD(IS, UTMRCS, 9, 1)
>> +    FIELD(IS, UCCS, 10, 1)
>> +    FIELD(IS, DFES, 11, 1)
>> +    FIELD(IS, UTPES, 12, 1)
>> +    FIELD(IS, HCFES, 16, 1)
>> +    FIELD(IS, SBFES, 17, 1)
>> +    FIELD(IS, CEFES, 18, 1)
>> +REG32(IE, offsetof(UfsReg, ie))
>> +REG32(HCS, offsetof(UfsReg, hcs))
>> +    FIELD(HCS, DP, 0, 1)
>> +    FIELD(HCS, UTRLRDY, 1, 1)
>> +    FIELD(HCS, UTMRLRDY, 2, 1)
>> +    FIELD(HCS, UCRDY, 3, 1)
>> +    FIELD(HCS, UPMCRS, 8, 3)
>> +REG32(HCE, offsetof(UfsReg, hce))
>> +    FIELD(HCE, HCE, 0, 1)
>> +    FIELD(HCE, CGE, 1, 1)
>> +REG32(UECPA, offsetof(UfsReg, uecpa))
>> +REG32(UECDL, offsetof(UfsReg, uecdl))
>> +REG32(UECN, offsetof(UfsReg, uecn))
>> +REG32(UECT, offsetof(UfsReg, uect))
>> +REG32(UECDME, offsetof(UfsReg, uecdme))
>> +REG32(UTRIACR, offsetof(UfsReg, utriacr))
>> +REG32(UTRLBA, offsetof(UfsReg, utrlba))
>> +    FIELD(UTRLBA, UTRLBA, 9, 22)
>> +REG32(UTRLBAU, offsetof(UfsReg, utrlbau))
>> +REG32(UTRLDBR, offsetof(UfsReg, utrldbr))
>> +REG32(UTRLCLR, offsetof(UfsReg, utrlclr))
>> +REG32(UTRLRSR, offsetof(UfsReg, utrlrsr))
>> +REG32(UTRLCNR, offsetof(UfsReg, utrlcnr))
>> +REG32(UTMRLBA, offsetof(UfsReg, utmrlba))
>> +    FIELD(UTMRLBA, UTMRLBA, 9, 22)
>> +REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau))
>> +REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr))
>> +REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr))
>> +REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr))
>> +REG32(UICCMD, offsetof(UfsReg, uiccmd))
>> +REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1))
>> +REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2))
>> +REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3))
>> +REG32(CCAP, offsetof(UfsReg, ccap))
>> +
>> +#define UFS_INTR_MASK                                    \
>> +    ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
>> +     (1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \
>> +     (1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) |   \
>> +     (1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \
>> +     (1 << R_IS_ULLS_SHIFT) | (1 << R_IS_UHES_SHIFT) |   \
>> +     (1 << R_IS_UHXS_SHIFT) | (1 << R_IS_UPMS_SHIFT) |   \
>> +     (1 << R_IS_UTMS_SHIFT) | (1 << R_IS_UE_SHIFT) |     \
>> +     (1 << R_IS_UDEPRI_SHIFT) | (1 << R_IS_UTRCS_SHIFT))
>> +
>> +#define UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT 24
>> +#define UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK 0xff
>> +#define UFS_UPIU_HEADER_TRANSACTION_TYPE(dword0)                       \
>> +    ((be32_to_cpu(dword0) >> UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT) & \
>> +     UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK)
>> +
>> +#define UFS_UPIU_HEADER_QUERY_FUNC_SHIFT 16
>> +#define UFS_UPIU_HEADER_QUERY_FUNC_MASK 0xff
>> +#define UFS_UPIU_HEADER_QUERY_FUNC(dword1)                       \
>> +    ((be32_to_cpu(dword1) >> UFS_UPIU_HEADER_QUERY_FUNC_SHIFT) & \
>> +     UFS_UPIU_HEADER_QUERY_FUNC_MASK)
>> +
>> +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT 0
>> +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK 0xffff
>> +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH(dword2)                       \
>> +    ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \
>> +     UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK)
>> +
>> +typedef struct QEMU_PACKED DeviceDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint8_t device;
>> +    uint8_t device_class;
>> +    uint8_t device_sub_class;
>> +    uint8_t protocol;
>> +    uint8_t number_lu;
>> +    uint8_t number_wlu;
>> +    uint8_t boot_enable;
>> +    uint8_t descr_access_en;
>> +    uint8_t init_power_mode;
>> +    uint8_t high_priority_lun;
>> +    uint8_t secure_removal_type;
>> +    uint8_t security_lu;
>> +    uint8_t background_ops_term_lat;
>> +    uint8_t init_active_icc_level;
>> +    uint16_t spec_version;
>> +    uint16_t manufacture_date;
>> +    uint8_t manufacturer_name;
>> +    uint8_t product_name;
>> +    uint8_t serial_number;
>> +    uint8_t oem_id;
>> +    uint16_t manufacturer_id;
>> +    uint8_t ud_0_base_offset;
>> +    uint8_t ud_config_p_length;
>> +    uint8_t device_rtt_cap;
>> +    uint16_t periodic_rtc_update;
>> +    uint8_t ufs_features_support;
>> +    uint8_t ffu_timeout;
>> +    uint8_t queue_depth;
>> +    uint16_t device_version;
>> +    uint8_t num_secure_wp_area;
>> +    uint32_t psa_max_data_size;
>> +    uint8_t psa_state_timeout;
>> +    uint8_t product_revision_level;
>> +    uint8_t reserved[36];
>> +    uint32_t extended_ufs_features_support;
>> +    uint8_t write_booster_buffer_preserve_user_space_en;
>> +    uint8_t write_booster_buffer_type;
>> +    uint32_t num_shared_write_booster_buffer_alloc_units;
>> +} DeviceDescriptor;
>> +
>> +typedef struct QEMU_PACKED GeometryDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint8_t media_technology;
>> +    uint8_t reserved;
>> +    uint64_t total_raw_device_capacity;
>> +    uint8_t max_number_lu;
>> +    uint32_t segment_size;
>> +    uint8_t allocation_unit_size;
>> +    uint8_t min_addr_block_size;
>> +    uint8_t optimal_read_block_size;
>> +    uint8_t optimal_write_block_size;
>> +    uint8_t max_in_buffer_size;
>> +    uint8_t max_out_buffer_size;
>> +    uint8_t rpmb_read_write_size;
>> +    uint8_t dynamic_capacity_resource_policy;
>> +    uint8_t data_ordering;
>> +    uint8_t max_context_id_number;
>> +    uint8_t sys_data_tag_unit_size;
>> +    uint8_t sys_data_tag_res_size;
>> +    uint8_t supported_sec_r_types;
>> +    uint16_t supported_memory_types;
>> +    uint32_t system_code_max_n_alloc_u;
>> +    uint16_t system_code_cap_adj_fac;
>> +    uint32_t non_persist_max_n_alloc_u;
>> +    uint16_t non_persist_cap_adj_fac;
>> +    uint32_t enhanced_1_max_n_alloc_u;
>> +    uint16_t enhanced_1_cap_adj_fac;
>> +    uint32_t enhanced_2_max_n_alloc_u;
>> +    uint16_t enhanced_2_cap_adj_fac;
>> +    uint32_t enhanced_3_max_n_alloc_u;
>> +    uint16_t enhanced_3_cap_adj_fac;
>> +    uint32_t enhanced_4_max_n_alloc_u;
>> +    uint16_t enhanced_4_cap_adj_fac;
>> +    uint32_t optimal_logical_block_size;
>> +    uint8_t reserved2[7];
>> +    uint32_t write_booster_buffer_max_n_alloc_units;
>> +    uint8_t device_max_write_booster_l_us;
>> +    uint8_t write_booster_buffer_cap_adj_fac;
>> +    uint8_t supported_write_booster_buffer_user_space_reduction_types;
>> +    uint8_t supported_write_booster_buffer_types;
>> +} GeometryDescriptor;
>> +
>> +#define UFS_GEOMETRY_CAPACITY_SHIFT 9
>> +
>> +typedef struct QEMU_PACKED UnitDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint8_t unit_index;
>> +    uint8_t lu_enable;
>> +    uint8_t boot_lun_id;
>> +    uint8_t lu_write_protect;
>> +    uint8_t lu_queue_depth;
>> +    uint8_t psa_sensitive;
>> +    uint8_t memory_type;
>> +    uint8_t data_reliability;
>> +    uint8_t logical_block_size;
>> +    uint64_t logical_block_count;
>> +    uint32_t erase_block_size;
>> +    uint8_t provisioning_type;
>> +    uint64_t phy_mem_resource_count;
>> +    uint16_t context_capabilities;
>> +    uint8_t large_unit_granularity_m1;
>> +    uint8_t reserved[6];
>> +    uint32_t lu_num_write_booster_buffer_alloc_units;
>> +} UnitDescriptor;
>> +
>> +typedef struct QEMU_PACKED RpmbUnitDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint8_t unit_index;
>> +    uint8_t lu_enable;
>> +    uint8_t boot_lun_id;
>> +    uint8_t lu_write_protect;
>> +    uint8_t lu_queue_depth;
>> +    uint8_t psa_sensitive;
>> +    uint8_t memory_type;
>> +    uint8_t reserved;
>> +    uint8_t logical_block_size;
>> +    uint64_t logical_block_count;
>> +    uint32_t erase_block_size;
>> +    uint8_t provisioning_type;
>> +    uint64_t phy_mem_resource_count;
>> +    uint8_t reserved2[3];
>> +} RpmbUnitDescriptor;
>> +
>> +typedef struct QEMU_PACKED PowerParametersDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint16_t active_icc_levels_vcc[16];
>> +    uint16_t active_icc_levels_vccq[16];
>> +    uint16_t active_icc_levels_vccq_2[16];
>> +} PowerParametersDescriptor;
>> +
>> +typedef struct QEMU_PACKED InterconnectDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint16_t bcd_unipro_version;
>> +    uint16_t bcd_mphy_version;
>> +} InterconnectDescriptor;
>> +
>> +typedef struct QEMU_PACKED StringDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint16_t UC[126];
>> +} StringDescriptor;
>> +
>> +typedef struct QEMU_PACKED DeviceHealthDescriptor {
>> +    uint8_t length;
>> +    uint8_t descriptor_idn;
>> +    uint8_t pre_eol_info;
>> +    uint8_t device_life_time_est_a;
>> +    uint8_t device_life_time_est_b;
>> +    uint8_t vendor_prop_info[32];
>> +    uint32_t refresh_total_count;
>> +    uint32_t refresh_progress;
>> +} DeviceHealthDescriptor;
>> +
>> +typedef struct QEMU_PACKED Flags {
>> +    uint8_t reserved;
>> +    uint8_t device_init;
>> +    uint8_t permanent_wp_en;
>> +    uint8_t power_on_wp_en;
>> +    uint8_t background_ops_en;
>> +    uint8_t device_life_span_mode_en;
>> +    uint8_t purge_enable;
>> +    uint8_t refresh_enable;
>> +    uint8_t phy_resource_removal;
>> +    uint8_t busy_rtc;
>> +    uint8_t reserved2;
>> +    uint8_t permanently_disable_fw_update;
>> +    uint8_t reserved3[2];
>> +    uint8_t wb_en;
>> +    uint8_t wb_buffer_flush_en;
>> +    uint8_t wb_buffer_flush_during_hibernate;
>> +    uint8_t reserved4[2];
>> +} Flags;
>> +
>> +typedef struct Attributes {
>> +    uint8_t boot_lun_en;
>> +    uint8_t reserved;
>> +    uint8_t current_power_mode;
>> +    uint8_t active_icc_level;
>> +    uint8_t out_of_order_data_en;
>> +    uint8_t background_op_status;
>> +    uint8_t purge_status;
>> +    uint8_t max_data_in_size;
>> +    uint8_t max_data_out_size;
>> +    uint32_t dyn_cap_needed;
>> +    uint8_t ref_clk_freq;
>> +    uint8_t config_descr_lock;
>> +    uint8_t max_num_of_rtt;
>> +    uint16_t exception_event_control;
>> +    uint16_t exception_event_status;
>> +    uint32_t seconds_passed;
>> +    uint16_t context_conf;
>> +    uint8_t device_ffu_status;
>> +    uint8_t psa_state;
>> +    uint32_t psa_data_size;
>> +    uint8_t ref_clk_gating_wait_time;
>> +    uint8_t device_case_rough_temperaure;
>> +    uint8_t device_too_high_temp_boundary;
>> +    uint8_t device_too_low_temp_boundary;
>> +    uint8_t throttling_status;
>> +    uint8_t wb_buffer_flush_status;
>> +    uint8_t available_wb_buffer_size;
>> +    uint8_t wb_buffer_life_time_est;
>> +    uint32_t current_wb_buffer_size;
>> +    uint8_t refresh_status;
>> +    uint8_t refresh_freq;
>> +    uint8_t refresh_unit;
>> +    uint8_t refresh_method;
>> +} Attributes;
>> +
>> +#define UFS_TRANSACTION_SPECIFIC_FIELD_SIZE 20
>> +#define UFS_MAX_QUERY_DATA_SIZE 256
>> +
>> +/* Command response result code */
>> +typedef enum CommandRespCode {
>> +    COMMAND_RESULT_SUCESS = 0x00,
>> +    COMMAND_RESULT_FAIL = 0x01,
>> +} CommandRespCode;
>> +
>> +enum {
>> +    UFS_UPIU_FLAG_UNDERFLOW = 0x20,
>> +    UFS_UPIU_FLAG_OVERFLOW = 0x40,
>> +};
>> +
>> +typedef struct QEMU_PACKED UtpUpiuHeader {
>> +    uint8_t trans_type;
>> +    uint8_t flags;
>> +    uint8_t lun;
>> +    uint8_t task_tag;
>> +    uint8_t iid_cmd_set_type;
>> +    uint8_t query_func;
>> +    uint8_t response;
>> +    uint8_t scsi_status;
>> +    uint8_t ehs_len;
>> +    uint8_t device_inf;
>> +    uint16_t data_segment_length;
>> +} UtpUpiuHeader;
>> +
>> +/*
>> + * The code below is copied from the linux kernel
>> + * ("include/uapi/scsi/scsi_bsg_ufs.h") and modified to fit the qemu style.
>> + */
>> +
>> +typedef struct QEMU_PACKED UtpUpiuQuery {
>> +    uint8_t opcode;
>> +    uint8_t idn;
>> +    uint8_t index;
>> +    uint8_t selector;
>> +    uint16_t reserved_osf;
>> +    uint16_t length;
>> +    uint32_t value;
>> +    uint32_t reserved[2];
>> +    /* EHS length should be 0. We don't have to worry about EHS area. */
>> +    uint8_t data[UFS_MAX_QUERY_DATA_SIZE];
>> +} UtpUpiuQuery;
>> +
>> +#define UFS_CDB_SIZE 16
>> +
>> +/*
>> + * struct UtpUpiuCmd - Command UPIU structure
>> + * @data_transfer_len: Data Transfer Length DW-3
>> + * @cdb: Command Descriptor Block CDB DW-4 to DW-7
>> + */
>> +typedef struct QEMU_PACKED UtpUpiuCmd {
>> +    uint32_t exp_data_transfer_len;
>> +    __uint8_t cdb[UFS_CDB_SIZE];
>
>Why is this __uint8_t instead of uint8_t? Was the code copied from
>another codebase and is the license GPLv2-or-later (this header file
>doesn't contain a license identifier)?
>

It is taken from linux code that has a GPLv2 license.
I'll modify __uint8_t to uint8_t and add the license statement with SPDX identifiers.

>> +} UtpUpiuCmd;
>> +
>> +/*
>> + * struct UtpUpiuReq - general upiu request structure
>> + * @header:UPIU header structure DW-0 to DW-2
>> + * @sc: fields structure for scsi command DW-3 to DW-7
>> + * @qr: fields structure for query request DW-3 to DW-7
>> + * @uc: use utp_upiu_query to host the 4 dwords of uic command
>> + */
>> +typedef struct QEMU_PACKED UtpUpiuReq {
>> +    UtpUpiuHeader header;
>> +    union {
>> +        UtpUpiuCmd sc;
>> +        UtpUpiuQuery qr;
>> +    };
>> +} UtpUpiuReq;
>> +
>> +/*
>> + * The code below is copied from the linux kernel ("include/ufs/ufshci.h") and
>> + * modified to fit the qemu style.
>> + */
>> +
>> +enum {
>> +    PWR_OK = 0x0,
>> +    PWR_LOCAL = 0x01,
>> +    PWR_REMOTE = 0x02,
>> +    PWR_BUSY = 0x03,
>> +    PWR_ERROR_CAP = 0x04,
>> +    PWR_FATAL_ERROR = 0x05,
>> +};
>> +
>> +/* UIC Commands */
>> +enum uic_cmd_dme {
>> +    UIC_CMD_DME_GET = 0x01,
>> +    UIC_CMD_DME_SET = 0x02,
>> +    UIC_CMD_DME_PEER_GET = 0x03,
>> +    UIC_CMD_DME_PEER_SET = 0x04,
>> +    UIC_CMD_DME_POWERON = 0x10,
>> +    UIC_CMD_DME_POWEROFF = 0x11,
>> +    UIC_CMD_DME_ENABLE = 0x12,
>> +    UIC_CMD_DME_RESET = 0x14,
>> +    UIC_CMD_DME_END_PT_RST = 0x15,
>> +    UIC_CMD_DME_LINK_STARTUP = 0x16,
>> +    UIC_CMD_DME_HIBER_ENTER = 0x17,
>> +    UIC_CMD_DME_HIBER_EXIT = 0x18,
>> +    UIC_CMD_DME_TEST_MODE = 0x1A,
>> +};
>> +
>> +/* UIC Config result code / Generic error code */
>> +enum {
>> +    UIC_CMD_RESULT_SUCCESS = 0x00,
>> +    UIC_CMD_RESULT_INVALID_ATTR = 0x01,
>> +    UIC_CMD_RESULT_FAILURE = 0x01,
>> +    UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
>> +    UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
>> +    UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
>> +    UIC_CMD_RESULT_BAD_INDEX = 0x05,
>> +    UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
>> +    UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
>> +    UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
>> +    UIC_CMD_RESULT_BUSY = 0x09,
>> +    UIC_CMD_RESULT_DME_FAILURE = 0x0A,
>> +};
>> +
>> +#define MASK_UIC_COMMAND_RESULT 0xFF
>> +
>> +/*
>> + * Request Descriptor Definitions
>> + */
>> +
>> +/* Transfer request command type */
>> +enum {
>> +    UTP_CMD_TYPE_SCSI = 0x0,
>> +    UTP_CMD_TYPE_UFS = 0x1,
>> +    UTP_CMD_TYPE_DEV_MANAGE = 0x2,
>> +};
>> +
>> +/* To accommodate UFS2.0 required Command type */
>> +enum {
>> +    UTP_CMD_TYPE_UFS_STORAGE = 0x1,
>> +};
>> +
>> +enum {
>> +    UTP_SCSI_COMMAND = 0x00000000,
>> +    UTP_NATIVE_UFS_COMMAND = 0x10000000,
>> +    UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
>> +    UTP_REQ_DESC_INT_CMD = 0x01000000,
>> +    UTP_REQ_DESC_CRYPTO_ENABLE_CMD = 0x00800000,
>> +};
>> +
>> +/* UTP Transfer Request Data Direction (DD) */
>> +enum {
>> +    UTP_NO_DATA_TRANSFER = 0x00000000,
>> +    UTP_HOST_TO_DEVICE = 0x02000000,
>> +    UTP_DEVICE_TO_HOST = 0x04000000,
>> +};
>> +
>> +/* Overall command status values */
>> +enum UtpOcsCodes {
>> +    OCS_SUCCESS = 0x0,
>> +    OCS_INVALID_CMD_TABLE_ATTR = 0x1,
>> +    OCS_INVALID_PRDT_ATTR = 0x2,
>> +    OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
>> +    OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
>> +    OCS_PEER_COMM_FAILURE = 0x5,
>> +    OCS_ABORTED = 0x6,
>> +    OCS_FATAL_ERROR = 0x7,
>> +    OCS_DEVICE_FATAL_ERROR = 0x8,
>> +    OCS_INVALID_CRYPTO_CONFIG = 0x9,
>> +    OCS_GENERAL_CRYPTO_ERROR = 0xa,
>> +    OCS_INVALID_COMMAND_STATUS = 0xf,
>> +};
>> +
>> +enum {
>> +    MASK_OCS = 0x0F,
>> +};
>> +
>> +/*
>> + * struct UfshcdSgEntry - UFSHCI PRD Entry
>> + * @addr: Physical address; DW-0 and DW-1.
>> + * @reserved: Reserved for future use DW-2
>> + * @size: size of physical segment DW-3
>> + */
>> +typedef struct QEMU_PACKED UfshcdSgEntry {
>> +    __le64 addr;
>> +    __le32 reserved;
>> +    __le32 size;
>> +    /*
>> +     * followed by variant-specific fields if
>> +     * CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE has been defined.
>> +     */
>> +} UfshcdSgEntry;
>> +
>> +/*
>> + * struct RequestDescHeader - Descriptor Header common to both UTRD and UTMRD
>> + * @dword0: Descriptor Header DW0
>> + * @dword1: Descriptor Header DW1
>> + * @dword2: Descriptor Header DW2
>> + * @dword3: Descriptor Header DW3
>> + */
>> +typedef struct QEMU_PACKED RequestDescHeader {
>> +    __le32 dword_0;
>> +    __le32 dword_1;
>> +    __le32 dword_2;
>> +    __le32 dword_3;
>> +} RequestDescHeader;
>
>docs/devel/style.rst:Don't use Linux kernel internal types like u32, __u32 or __le32.
>
>Please use uint32_t.
>

Okay, I'll fix it.

>> +
>> +/*
>> + * struct UtpTransferReqDesc - UTP Transfer Request Descriptor (UTRD)
>> + * @header: UTRD header DW-0 to DW-3
>> + * @command_desc_base_addr_lo: UCD base address low DW-4
>> + * @command_desc_base_addr_hi: UCD base address high DW-5
>> + * @response_upiu_length: response UPIU length DW-6
>> + * @response_upiu_offset: response UPIU offset DW-6
>> + * @prd_table_length: Physical region descriptor length DW-7
>> + * @prd_table_offset: Physical region descriptor offset DW-7
>> + */
>> +typedef struct QEMU_PACKED UtpTransferReqDesc {
>> +    /* DW 0-3 */
>> +    RequestDescHeader header;
>> +
>> +    /* DW 4-5*/
>> +    __le32 command_desc_base_addr_lo;
>> +    __le32 command_desc_base_addr_hi;
>> +
>> +    /* DW 6 */
>> +    __le16 response_upiu_length;
>> +    __le16 response_upiu_offset;
>> +
>> +    /* DW 7 */
>> +    __le16 prd_table_length;
>> +    __le16 prd_table_offset;
>> +} UtpTransferReqDesc;
>> +
>> +/*
>> + * The code below is copied from the linux kernel ("include/ufs/ufs.h") and
>> + * modified to fit the qemu style.
>> + */
>> +
>> +#define GENERAL_UPIU_REQUEST_SIZE (sizeof(UtpUpiuReq))
>> +#define QUERY_DESC_MAX_SIZE 255
>> +#define QUERY_DESC_MIN_SIZE 2
>> +#define QUERY_DESC_HDR_SIZE 2
>> +#define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - (sizeof(UtpUpiuHeader)))
>> +#define UFS_SENSE_SIZE 18
>> +
>> +#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0) \
>> +    cpu_to_be32((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | (byte0))
>> +/*
>> + * UFS device may have standard LUs and LUN id could be from 0x00 to
>> + * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
>> + * UFS device may also have the Well Known LUs (also referred as W-LU)
>> + * which again could be from 0x00 to 0x7F. For W-LUs, device only use
>> + * the "Extended Addressing Format" which means the W-LUNs would be
>> + * from 0xc100 (SCSI_W_LUN_BASE) onwards.
>> + * This means max. LUN number reported from UFS device could be 0xC17F.
>> + */
>> +#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F
>> +#define UFS_UPIU_WLUN_ID (1 << 7)
>> +
>> +/* WriteBooster buffer is available only for the logical unit from 0 to 7 */
>> +#define UFS_UPIU_MAX_WB_LUN_ID 8
>> +
>> +/*
>> + * WriteBooster buffer lifetime has a limit setted by vendor.
>> + * If it is over the limit, WriteBooster feature will be disabled.
>> + */
>> +#define UFS_WB_EXCEED_LIFETIME 0x0B
>> +
>> +/*
>> + * In UFS Spec, the Extra Header Segment (EHS) starts from byte 32 in UPIU
>> + * request/response packet
>> + */
>> +#define EHS_OFFSET_IN_RESPONSE 32
>> +
>> +/* Well known logical unit id in LUN field of UPIU */
>> +enum {
>> +    UFS_UPIU_REPORT_LUNS_WLUN = 0x81,
>> +    UFS_UPIU_UFS_DEVICE_WLUN = 0xD0,
>> +    UFS_UPIU_BOOT_WLUN = 0xB0,
>> +    UFS_UPIU_RPMB_WLUN = 0xC4,
>> +};
>> +
>> +/*
>> + * UFS Protocol Information Unit related definitions
>> + */
>> +
>> +/* Task management functions */
>> +enum {
>> +    UFS_ABORT_TASK = 0x01,
>> +    UFS_ABORT_TASK_SET = 0x02,
>> +    UFS_CLEAR_TASK_SET = 0x04,
>> +    UFS_LOGICAL_RESET = 0x08,
>> +    UFS_QUERY_TASK = 0x80,
>> +    UFS_QUERY_TASK_SET = 0x81,
>> +};
>> +
>> +/* UTP UPIU Transaction Codes Initiator to Target */
>> +enum {
>> +    UPIU_TRANSACTION_NOP_OUT = 0x00,
>> +    UPIU_TRANSACTION_COMMAND = 0x01,
>> +    UPIU_TRANSACTION_DATA_OUT = 0x02,
>> +    UPIU_TRANSACTION_TASK_REQ = 0x04,
>> +    UPIU_TRANSACTION_QUERY_REQ = 0x16,
>> +};
>> +
>> +/* UTP UPIU Transaction Codes Target to Initiator */
>> +enum {
>> +    UPIU_TRANSACTION_NOP_IN = 0x20,
>> +    UPIU_TRANSACTION_RESPONSE = 0x21,
>> +    UPIU_TRANSACTION_DATA_IN = 0x22,
>> +    UPIU_TRANSACTION_TASK_RSP = 0x24,
>> +    UPIU_TRANSACTION_READY_XFER = 0x31,
>> +    UPIU_TRANSACTION_QUERY_RSP = 0x36,
>> +    UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
>> +};
>> +
>> +/* UPIU Read/Write flags */
>> +enum {
>> +    UPIU_CMD_FLAGS_NONE = 0x00,
>> +    UPIU_CMD_FLAGS_WRITE = 0x20,
>> +    UPIU_CMD_FLAGS_READ = 0x40,
>> +};
>> +
>> +/* UPIU Task Attributes */
>> +enum {
>> +    UPIU_TASK_ATTR_SIMPLE = 0x00,
>> +    UPIU_TASK_ATTR_ORDERED = 0x01,
>> +    UPIU_TASK_ATTR_HEADQ = 0x02,
>> +    UPIU_TASK_ATTR_ACA = 0x03,
>> +};
>> +
>> +/* UPIU Query request function */
>> +enum {
>> +    UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
>> +    UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
>> +};
>> +
>> +/* Flag idn for Query Requests*/
>> +enum flag_idn {
>> +    QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
>> +    QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
>> +    QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
>> +    QUERY_FLAG_IDN_BKOPS_EN = 0x04,
>> +    QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
>> +    QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
>> +    QUERY_FLAG_IDN_REFRESH_ENABLE = 0x07,
>> +    QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
>> +    QUERY_FLAG_IDN_BUSY_RTC = 0x09,
>> +    QUERY_FLAG_IDN_RESERVED3 = 0x0A,
>> +    QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
>> +    QUERY_FLAG_IDN_WB_EN = 0x0E,
>> +    QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN = 0x0F,
>> +    QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8 = 0x10,
>> +    QUERY_FLAG_IDN_HPB_RESET = 0x11,
>> +    QUERY_FLAG_IDN_HPB_EN = 0x12,
>> +    QUERY_FLAG_IDN_COUNT,
>> +};
>> +
>> +/* Attribute idn for Query requests */
>> +enum attr_idn {
>> +    QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
>> +    QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD = 0x01,
>> +    QUERY_ATTR_IDN_POWER_MODE = 0x02,
>> +    QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
>> +    QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
>> +    QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
>> +    QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
>> +    QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
>> +    QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
>> +    QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
>> +    QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
>> +    QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
>> +    QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
>> +    QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
>> +    QUERY_ATTR_IDN_EE_STATUS = 0x0E,
>> +    QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
>> +    QUERY_ATTR_IDN_CNTX_CONF = 0x10,
>> +    QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
>> +    QUERY_ATTR_IDN_RESERVED2 = 0x12,
>> +    QUERY_ATTR_IDN_RESERVED3 = 0x13,
>> +    QUERY_ATTR_IDN_FFU_STATUS = 0x14,
>> +    QUERY_ATTR_IDN_PSA_STATE = 0x15,
>> +    QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
>> +    QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME = 0x17,
>> +    QUERY_ATTR_IDN_CASE_ROUGH_TEMP = 0x18,
>> +    QUERY_ATTR_IDN_HIGH_TEMP_BOUND = 0x19,
>> +    QUERY_ATTR_IDN_LOW_TEMP_BOUND = 0x1A,
>> +    QUERY_ATTR_IDN_THROTTLING_STATUS = 0x1B,
>> +    QUERY_ATTR_IDN_WB_FLUSH_STATUS = 0x1C,
>> +    QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D,
>> +    QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
>> +    QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
>> +    QUERY_ATTR_IDN_REFRESH_STATUS = 0x2C,
>> +    QUERY_ATTR_IDN_REFRESH_FREQ = 0x2D,
>> +    QUERY_ATTR_IDN_REFRESH_UNIT = 0x2E,
>> +    QUERY_ATTR_IDN_COUNT,
>> +};
>> +
>> +/* Descriptor idn for Query requests */
>> +enum desc_idn {
>> +    QUERY_DESC_IDN_DEVICE = 0x0,
>> +    QUERY_DESC_IDN_CONFIGURATION = 0x1,
>> +    QUERY_DESC_IDN_UNIT = 0x2,
>> +    QUERY_DESC_IDN_RFU_0 = 0x3,
>> +    QUERY_DESC_IDN_INTERCONNECT = 0x4,
>> +    QUERY_DESC_IDN_STRING = 0x5,
>> +    QUERY_DESC_IDN_RFU_1 = 0x6,
>> +    QUERY_DESC_IDN_GEOMETRY = 0x7,
>> +    QUERY_DESC_IDN_POWER = 0x8,
>> +    QUERY_DESC_IDN_HEALTH = 0x9,
>> +    QUERY_DESC_IDN_MAX,
>> +};
>> +
>> +enum desc_header_offset {
>> +    QUERY_DESC_LENGTH_OFFSET = 0x00,
>> +    QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
>> +};
>> +
>> +/* Unit descriptor parameters offsets in bytes*/
>> +enum unit_desc_param {
>> +    UNIT_DESC_PARAM_LEN = 0x0,
>> +    UNIT_DESC_PARAM_TYPE = 0x1,
>> +    UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
>> +    UNIT_DESC_PARAM_LU_ENABLE = 0x3,
>> +    UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
>> +    UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
>> +    UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
>> +    UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
>> +    UNIT_DESC_PARAM_MEM_TYPE = 0x8,
>> +    UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9,
>> +    UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
>> +    UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
>> +    UNIT_DESC_PARAM_ERASE_BLK_SIZE = 0x13,
>> +    UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
>> +    UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
>> +    UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20,
>> +    UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
>> +    UNIT_DESC_PARAM_HPB_LU_MAX_ACTIVE_RGNS = 0x23,
>> +    UNIT_DESC_PARAM_HPB_PIN_RGN_START_OFF = 0x25,
>> +    UNIT_DESC_PARAM_HPB_NUM_PIN_RGNS = 0x27,
>> +    UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29,
>> +};
>> +
>> +/* RPMB Unit descriptor parameters offsets in bytes*/
>> +enum rpmb_unit_desc_param {
>> +    RPMB_UNIT_DESC_PARAM_LEN = 0x0,
>> +    RPMB_UNIT_DESC_PARAM_TYPE = 0x1,
>> +    RPMB_UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
>> +    RPMB_UNIT_DESC_PARAM_LU_ENABLE = 0x3,
>> +    RPMB_UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
>> +    RPMB_UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
>> +    RPMB_UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
>> +    RPMB_UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
>> +    RPMB_UNIT_DESC_PARAM_MEM_TYPE = 0x8,
>> +    RPMB_UNIT_DESC_PARAM_REGION_EN = 0x9,
>> +    RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
>> +    RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
>> +    RPMB_UNIT_DESC_PARAM_REGION0_SIZE = 0x13,
>> +    RPMB_UNIT_DESC_PARAM_REGION1_SIZE = 0x14,
>> +    RPMB_UNIT_DESC_PARAM_REGION2_SIZE = 0x15,
>> +    RPMB_UNIT_DESC_PARAM_REGION3_SIZE = 0x16,
>> +    RPMB_UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
>> +    RPMB_UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
>> +};
>> +
>> +/* Device descriptor parameters offsets in bytes*/
>> +enum device_desc_param {
>> +    DEVICE_DESC_PARAM_LEN = 0x0,
>> +    DEVICE_DESC_PARAM_TYPE = 0x1,
>> +    DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2,
>> +    DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3,
>> +    DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4,
>> +    DEVICE_DESC_PARAM_PRTCL = 0x5,
>> +    DEVICE_DESC_PARAM_NUM_LU = 0x6,
>> +    DEVICE_DESC_PARAM_NUM_WLU = 0x7,
>> +    DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
>> +    DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9,
>> +    DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
>> +    DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB,
>> +    DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC,
>> +    DEVICE_DESC_PARAM_SEC_LU = 0xD,
>> +    DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE,
>> +    DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
>> +    DEVICE_DESC_PARAM_SPEC_VER = 0x10,
>> +    DEVICE_DESC_PARAM_MANF_DATE = 0x12,
>> +    DEVICE_DESC_PARAM_MANF_NAME = 0x14,
>> +    DEVICE_DESC_PARAM_PRDCT_NAME = 0x15,
>> +    DEVICE_DESC_PARAM_SN = 0x16,
>> +    DEVICE_DESC_PARAM_OEM_ID = 0x17,
>> +    DEVICE_DESC_PARAM_MANF_ID = 0x18,
>> +    DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
>> +    DEVICE_DESC_PARAM_UD_LEN = 0x1B,
>> +    DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
>> +    DEVICE_DESC_PARAM_FRQ_RTC = 0x1D,
>> +    DEVICE_DESC_PARAM_UFS_FEAT = 0x1F,
>> +    DEVICE_DESC_PARAM_FFU_TMT = 0x20,
>> +    DEVICE_DESC_PARAM_Q_DPTH = 0x21,
>> +    DEVICE_DESC_PARAM_DEV_VER = 0x22,
>> +    DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24,
>> +    DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25,
>> +    DEVICE_DESC_PARAM_PSA_TMT = 0x29,
>> +    DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
>> +    DEVICE_DESC_PARAM_HPB_VER = 0x40,
>> +    DEVICE_DESC_PARAM_HPB_CONTROL = 0x42,
>> +    DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
>> +    DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53,
>> +    DEVICE_DESC_PARAM_WB_TYPE = 0x54,
>> +    DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55,
>> +};
>> +
>> +/* Interconnect descriptor parameters offsets in bytes*/
>> +enum interconnect_desc_param {
>> +    INTERCONNECT_DESC_PARAM_LEN = 0x0,
>> +    INTERCONNECT_DESC_PARAM_TYPE = 0x1,
>> +    INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2,
>> +    INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4,
>> +};
>> +
>> +/* Geometry descriptor parameters offsets in bytes*/
>> +enum geometry_desc_param {
>> +    GEOMETRY_DESC_PARAM_LEN = 0x0,
>> +    GEOMETRY_DESC_PARAM_TYPE = 0x1,
>> +    GEOMETRY_DESC_PARAM_DEV_CAP = 0x4,
>> +    GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC,
>> +    GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD,
>> +    GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11,
>> +    GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12,
>> +    GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13,
>> +    GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14,
>> +    GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15,
>> +    GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16,
>> +    GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17,
>> +    GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18,
>> +    GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19,
>> +    GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A,
>> +    GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B,
>> +    GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C,
>> +    GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D,
>> +    GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E,
>> +    GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20,
>> +    GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24,
>> +    GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26,
>> +    GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A,
>> +    GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C,
>> +    GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30,
>> +    GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32,
>> +    GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36,
>> +    GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38,
>> +    GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C,
>> +    GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E,
>> +    GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42,
>> +    GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44,
>> +    GEOMETRY_DESC_PARAM_HPB_REGION_SIZE = 0x48,
>> +    GEOMETRY_DESC_PARAM_HPB_NUMBER_LU = 0x49,
>> +    GEOMETRY_DESC_PARAM_HPB_SUBREGION_SIZE = 0x4A,
>> +    GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS = 0x4B,
>> +    GEOMETRY_DESC_PARAM_WB_MAX_ALLOC_UNITS = 0x4F,
>> +    GEOMETRY_DESC_PARAM_WB_MAX_WB_LUNS = 0x53,
>> +    GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54,
>> +    GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55,
>> +    GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56,
>> +};
>> +
>> +/* Health descriptor parameters offsets in bytes*/
>> +enum health_desc_param {
>> +    HEALTH_DESC_PARAM_LEN = 0x0,
>> +    HEALTH_DESC_PARAM_TYPE = 0x1,
>> +    HEALTH_DESC_PARAM_EOL_INFO = 0x2,
>> +    HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3,
>> +    HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
>> +};
>> +
>> +/* WriteBooster buffer mode */
>> +enum {
>> +    WB_BUF_MODE_LU_DEDICATED = 0x0,
>> +    WB_BUF_MODE_SHARED = 0x1,
>> +};
>> +
>> +/*
>> + * Logical Unit Write Protect
>> + * 00h: LU not write protected
>> + * 01h: LU write protected when fPowerOnWPEn =1
>> + * 02h: LU permanently write protected when fPermanentWPEn =1
>> + */
>> +enum ufs_lu_wp_type {
>> +    UFS_LU_NO_WP = 0x00,
>> +    UFS_LU_POWER_ON_WP = 0x01,
>> +    UFS_LU_PERM_WP = 0x02,
>> +};
>> +
>> +/* UTP QUERY Transaction Specific Fields OpCode */
>> +enum query_opcode {
>> +    UPIU_QUERY_OPCODE_NOP = 0x0,
>> +    UPIU_QUERY_OPCODE_READ_DESC = 0x1,
>> +    UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
>> +    UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
>> +    UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
>> +    UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
>> +    UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
>> +    UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
>> +    UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
>> +};
>> +
>> +/* Query response result code */
>> +typedef enum QueryRespCode {
>> +    QUERY_RESULT_SUCCESS = 0x00,
>> +    QUERY_RESULT_NOT_READABLE = 0xF6,
>> +    QUERY_RESULT_NOT_WRITEABLE = 0xF7,
>> +    QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
>> +    QUERY_RESULT_INVALID_LENGTH = 0xF9,
>> +    QUERY_RESULT_INVALID_VALUE = 0xFA,
>> +    QUERY_RESULT_INVALID_SELECTOR = 0xFB,
>> +    QUERY_RESULT_INVALID_INDEX = 0xFC,
>> +    QUERY_RESULT_INVALID_IDN = 0xFD,
>> +    QUERY_RESULT_INVALID_OPCODE = 0xFE,
>> +    QUERY_RESULT_GENERAL_FAILURE = 0xFF,
>> +} QueryRespCode;
>> +
>> +/* UTP Transfer Request Command Type (CT) */
>> +enum {
>> +    UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
>> +    UPIU_COMMAND_SET_TYPE_UFS = 0x1,
>> +    UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
>> +};
>> +
>> +/* Task management service response */
>> +enum {
>> +    UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00,
>> +    UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
>> +    UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08,
>> +    UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
>> +    UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
>> +};
>> +
>> +/* UFS device power modes */
>> +enum ufs_dev_pwr_mode {
>> +    UFS_ACTIVE_PWR_MODE = 1,
>> +    UFS_SLEEP_PWR_MODE = 2,
>> +    UFS_POWERDOWN_PWR_MODE = 3,
>> +    UFS_DEEPSLEEP_PWR_MODE = 4,
>> +};
>> +
>> +/*
>> + * struct UtpCmdRsp - Response UPIU structure
>> + * @residual_transfer_count: Residual transfer count DW-3
>> + * @reserved: Reserved double words DW-4 to DW-7
>> + * @sense_data_len: Sense data length DW-8 U16
>> + * @sense_data: Sense data field DW-8 to DW-12
>> + */
>> +typedef struct QEMU_PACKED UtpCmdRsp {
>> +    uint32_t residual_transfer_count;
>> +    uint32_t reserved[4];
>> +    uint16_t sense_data_len;
>> +    uint8_t sense_data[UFS_SENSE_SIZE];
>> +} UtpCmdRsp;
>> +
>> +/*
>> + * struct UtpUpiuRsp - general upiu response structure
>> + * @header: UPIU header structure DW-0 to DW-2
>> + * @sr: fields structure for scsi command DW-3 to DW-12
>> + * @qr: fields structure for query request DW-3 to DW-7
>> + */
>> +typedef struct QEMU_PACKED UtpUpiuRsp {
>> +    UtpUpiuHeader header;
>> +    union {
>> +        UtpCmdRsp sr;
>> +        UtpUpiuQuery qr;
>> +    };
>> +} UtpUpiuRsp;
>> +
>> +static inline void _ufs_check_size(void)
>> +{
>> +    QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104);
>> +    QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89);
>> +    QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87);
>> +    QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45);
>> +    QEMU_BUILD_BUG_ON(sizeof(RpmbUnitDescriptor) != 35);
>> +    QEMU_BUILD_BUG_ON(sizeof(PowerParametersDescriptor) != 98);
>> +    QEMU_BUILD_BUG_ON(sizeof(InterconnectDescriptor) != 6);
>> +    QEMU_BUILD_BUG_ON(sizeof(StringDescriptor) != 254);
>> +    QEMU_BUILD_BUG_ON(sizeof(DeviceHealthDescriptor) != 45);
>> +    QEMU_BUILD_BUG_ON(sizeof(Flags) != 0x13);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuHeader) != 12);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuQuery) != 276);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuCmd) != 20);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuReq) != 288);
>> +    QEMU_BUILD_BUG_ON(sizeof(UfshcdSgEntry) != 16);
>> +    QEMU_BUILD_BUG_ON(sizeof(RequestDescHeader) != 16);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpTransferReqDesc) != 32);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40);
>> +    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288);
>> +}
>> +#endif
>> diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
>> index e6d0574a29..1094274546 100644
>> --- a/include/hw/pci/pci.h
>> +++ b/include/hw/pci/pci.h
>> @@ -114,6 +114,7 @@ extern bool pci_available;
>>  #define PCI_DEVICE_ID_REDHAT_NVME        0x0010
>>  #define PCI_DEVICE_ID_REDHAT_PVPANIC     0x0011
>>  #define PCI_DEVICE_ID_REDHAT_ACPI_ERST   0x0012
>> +#define PCI_DEVICE_ID_REDHAT_UFS         0x0013
>
>Please update docs/specs/pci-ids.rst in order to reserve this device ID.
>

Okay :)

>>  #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
>>  
>>  #define FMT_PCIBUS                      PRIx64
>> diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
>> index e4386ebb20..85469b9b53 100644
>> --- a/include/hw/pci/pci_ids.h
>> +++ b/include/hw/pci/pci_ids.h
>> @@ -26,6 +26,7 @@
>>  #define PCI_CLASS_STORAGE_SATA           0x0106
>>  #define PCI_CLASS_STORAGE_SAS            0x0107
>>  #define PCI_CLASS_STORAGE_EXPRESS        0x0108
>> +#define PCI_CLASS_STORAGE_UFS            0x0109
>>  #define PCI_CLASS_STORAGE_OTHER          0x0180
>>  
>>  #define PCI_BASE_CLASS_NETWORK           0x02
>> diff --git a/meson.build b/meson.build
>> index 34306a6205..6aeaa155db 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -3268,6 +3268,7 @@ if have_system
>>      'hw/ssi',
>>      'hw/timer',
>>      'hw/tpm',
>> +    'hw/ufs',
>>      'hw/usb',
>>      'hw/vfio',
>>      'hw/virtio',
>> -- 
>> 2.34.1
>> 

I will create a new version of patches as soon as reviews of other patches are completed.
Thank you for your detailed review!
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 88b5a7ee0a..91c2bfbb09 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2231,6 +2231,12 @@  F: tests/qtest/nvme-test.c
 F: docs/system/devices/nvme.rst
 T: git git://git.infradead.org/qemu-nvme.git nvme-next
 
+ufs
+M: Jeuk Kim <jeuk20.kim@samsung.com>
+S: Supported
+F: hw/ufs/*
+F: include/block/ufs.h
+
 megasas
 M: Hannes Reinecke <hare@suse.com>
 L: qemu-block@nongnu.org
diff --git a/hw/Kconfig b/hw/Kconfig
index ba62ff6417..9ca7b38c31 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -38,6 +38,7 @@  source smbios/Kconfig
 source ssi/Kconfig
 source timer/Kconfig
 source tpm/Kconfig
+source ufs/Kconfig
 source usb/Kconfig
 source virtio/Kconfig
 source vfio/Kconfig
diff --git a/hw/meson.build b/hw/meson.build
index c7ac7d3d75..f01fac4617 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -37,6 +37,7 @@  subdir('smbios')
 subdir('ssi')
 subdir('timer')
 subdir('tpm')
+subdir('ufs')
 subdir('usb')
 subdir('vfio')
 subdir('virtio')
diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig
new file mode 100644
index 0000000000..b7b3392e85
--- /dev/null
+++ b/hw/ufs/Kconfig
@@ -0,0 +1,4 @@ 
+config UFS_PCI
+    bool
+    default y if PCI_DEVICES
+    depends on PCI
diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build
new file mode 100644
index 0000000000..c1d90eeea6
--- /dev/null
+++ b/hw/ufs/meson.build
@@ -0,0 +1 @@ 
+softmmu_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c'))
diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
new file mode 100644
index 0000000000..17793929b1
--- /dev/null
+++ b/hw/ufs/trace-events
@@ -0,0 +1,33 @@ 
+# ufs.c
+ufs_irq_raise(void) "INTx"
+ufs_irq_lower(void) "INTx"
+ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
+ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
+ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32""
+ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
+ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
+ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
+ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32""
+ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8""
+ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8""
+ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32""
+
+# error condition
+ufs_err_memory_allocation(void) "failed to allocate memory"
+ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
+ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""
+ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
+ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
+ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64""
+ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy"
+ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported"
+ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid"
+ufs_err_scsi_cmd_invalid_lun(uint8_t lun) "scsi command has invalid lun: 0x%"PRIx8""
+ufs_err_query_flag_not_readable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to read"
+ufs_err_query_flag_not_writable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to write"
+ufs_err_query_attr_not_readable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to read"
+ufs_err_query_attr_not_writable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to write"
+ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. opcode: 0x%"PRIx8""
+ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8""
+ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8""
+ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8""
diff --git a/hw/ufs/trace.h b/hw/ufs/trace.h
new file mode 100644
index 0000000000..2dbd6397c3
--- /dev/null
+++ b/hw/ufs/trace.h
@@ -0,0 +1 @@ 
+#include "trace/trace-hw_ufs.h"
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
new file mode 100644
index 0000000000..9dba1073a8
--- /dev/null
+++ b/hw/ufs/ufs.c
@@ -0,0 +1,305 @@ 
+/*
+ * QEMU Universal Flash Storage (UFS) Controller
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Written by Jeuk Kim <jeuk20.kim@samsung.com>
+ *
+ * This code is licensed under the GNU GPL v2 or later.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+#include "ufs.h"
+
+/* The QEMU-UFS device follows spec version 3.1 */
+#define UFS_SPEC_VER 0x00000310
+#define UFS_MAX_NUTRS 32
+#define UFS_MAX_NUTMRS 8
+
+static void ufs_irq_check(UfsHc *u)
+{
+    PCIDevice *pci = PCI_DEVICE(u);
+    uint32_t is = ldl_le_p(&u->reg.is);
+    uint32_t ie = ldl_le_p(&u->reg.ie);
+
+    if ((is & UFS_INTR_MASK) & ie) {
+        trace_ufs_irq_raise();
+        pci_irq_assert(pci);
+    } else {
+        trace_ufs_irq_lower();
+        pci_irq_deassert(pci);
+    }
+}
+
+static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
+{
+    uint32_t is = ldl_le_p(&u->reg.is);
+    uint32_t hcs = ldl_le_p(&u->reg.hcs);
+    uint32_t ucmdarg1 = ldl_le_p(&u->reg.ucmdarg1);
+    uint32_t ucmdarg2 = ldl_le_p(&u->reg.ucmdarg2);
+    uint32_t ucmdarg3 = ldl_le_p(&u->reg.ucmdarg3);
+
+    trace_ufs_process_uiccmd(val, ucmdarg1, ucmdarg2, ucmdarg3);
+    /*
+     * Only the essential uic commands for running drivers on Linux and Windows
+     * are implemented.
+     */
+    switch (val) {
+    case UIC_CMD_DME_LINK_STARTUP:
+        hcs = FIELD_DP32(hcs, HCS, DP, 1);
+        hcs = FIELD_DP32(hcs, HCS, UTRLRDY, 1);
+        hcs = FIELD_DP32(hcs, HCS, UTMRLRDY, 1);
+        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
+        break;
+    /* TODO: Revisit it when Power Management is implemented */
+    case UIC_CMD_DME_HIBER_ENTER:
+        is = FIELD_DP32(is, IS, UHES, 1);
+        hcs = FIELD_DP32(hcs, HCS, UPMCRS, PWR_LOCAL);
+        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
+        break;
+    case UIC_CMD_DME_HIBER_EXIT:
+        is = FIELD_DP32(is, IS, UHXS, 1);
+        hcs = FIELD_DP32(hcs, HCS, UPMCRS, PWR_LOCAL);
+        ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
+        break;
+    default:
+        ucmdarg2 = UIC_CMD_RESULT_FAILURE;
+    }
+
+    is = FIELD_DP32(is, IS, UCCS, 1);
+
+    stl_le_p(&u->reg.is, is);
+    stl_le_p(&u->reg.hcs, hcs);
+    stl_le_p(&u->reg.ucmdarg1, ucmdarg1);
+    stl_le_p(&u->reg.ucmdarg2, ucmdarg2);
+    stl_le_p(&u->reg.ucmdarg3, ucmdarg3);
+
+    ufs_irq_check(u);
+}
+
+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 utrlcnr = ldl_le_p(&u->reg.utrlcnr);
+    uint32_t utrlba, utmrlba;
+
+    switch (offset) {
+    case A_IS:
+        is &= ~data;
+        stl_le_p(&u->reg.is, is);
+        ufs_irq_check(u);
+        break;
+    case A_IE:
+        stl_le_p(&u->reg.ie, data);
+        ufs_irq_check(u);
+        break;
+    case A_HCE:
+        if (!FIELD_EX32(hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) {
+            hcs = FIELD_DP32(hcs, HCS, UCRDY, 1);
+            hce = FIELD_DP32(hce, HCE, HCE, 1);
+            stl_le_p(&u->reg.hcs, hcs);
+            stl_le_p(&u->reg.hce, hce);
+        } else if (FIELD_EX32(hce, HCE, HCE) && !FIELD_EX32(data, HCE, HCE)) {
+            hcs = 0;
+            hce = FIELD_DP32(hce, HCE, HCE, 0);
+            stl_le_p(&u->reg.hcs, hcs);
+            stl_le_p(&u->reg.hce, hce);
+        }
+        break;
+    case A_UTRLBA:
+        utrlba = data & R_UTRLBA_UTRLBA_MASK;
+        stl_le_p(&u->reg.utrlba, utrlba);
+        break;
+    case A_UTRLBAU:
+        stl_le_p(&u->reg.utrlbau, data);
+        break;
+    case A_UTRLDBR:
+        /* Not yet supported */
+        break;
+    case A_UTRLRSR:
+        stl_le_p(&u->reg.utrlrsr, data);
+        break;
+    case A_UTRLCNR:
+        utrlcnr &= ~data;
+        stl_le_p(&u->reg.utrlcnr, utrlcnr);
+        break;
+    case A_UTMRLBA:
+        utmrlba = data & R_UTMRLBA_UTMRLBA_MASK;
+        stl_le_p(&u->reg.utmrlba, utmrlba);
+        break;
+    case A_UTMRLBAU:
+        stl_le_p(&u->reg.utmrlbau, data);
+        break;
+    case A_UICCMD:
+        ufs_process_uiccmd(u, data);
+        break;
+    case A_UCMDARG1:
+        stl_le_p(&u->reg.ucmdarg1, data);
+        break;
+    case A_UCMDARG2:
+        stl_le_p(&u->reg.ucmdarg2, data);
+        break;
+    case A_UCMDARG3:
+        stl_le_p(&u->reg.ucmdarg3, data);
+        break;
+    case A_UTRLCLR:
+    case A_UTMRLDBR:
+    case A_UTMRLCLR:
+    case A_UTMRLRSR:
+        trace_ufs_err_unsupport_register_offset(offset);
+        break;
+    default:
+        trace_ufs_err_invalid_register_offset(offset);
+        break;
+    }
+}
+
+static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    UfsHc *u = (UfsHc *)opaque;
+    uint8_t *ptr = (uint8_t *)&u->reg;
+    uint64_t value;
+
+    if (addr > sizeof(u->reg) - size) {
+        trace_ufs_err_invalid_register_offset(addr);
+        return 0;
+    }
+
+    value = ldn_le_p(ptr + addr, size);
+    trace_ufs_mmio_read(addr, value, size);
+    return value;
+}
+
+static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
+                           unsigned size)
+{
+    UfsHc *u = (UfsHc *)opaque;
+
+    if (addr > sizeof(u->reg) - size) {
+        trace_ufs_err_invalid_register_offset(addr);
+        return;
+    }
+
+    trace_ufs_mmio_write(addr, data, size);
+    ufs_write_reg(u, addr, data, size);
+}
+
+static const MemoryRegionOps ufs_mmio_ops = {
+    .read = ufs_mmio_read,
+    .write = ufs_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static bool ufs_check_constraints(UfsHc *u, Error **errp)
+{
+    if (u->params.nutrs > UFS_MAX_NUTRS) {
+        error_setg(errp, "nutrs must be less than %d", UFS_MAX_NUTRS);
+        return false;
+    }
+
+    if (u->params.nutmrs > UFS_MAX_NUTMRS) {
+        error_setg(errp, "nutmrs must be less than %d", UFS_MAX_NUTMRS);
+        return false;
+    }
+
+    return true;
+}
+
+static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
+{
+    uint8_t *pci_conf = pci_dev->config;
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1;
+    pci_config_set_prog_interface(pci_conf, 0x1);
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REDHAT_UFS);
+
+    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_UFS);
+
+    memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
+                          u->reg_size);
+    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
+    u->irq = pci_allocate_irq(pci_dev);
+}
+
+static void ufs_init_hc(UfsHc *u)
+{
+    uint32_t cap = 0;
+
+    u->reg_size = pow2ceil(sizeof(UfsReg));
+
+    memset(&u->reg, 0, sizeof(u->reg));
+    cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
+    cap = FIELD_DP32(cap, CAP, RTT, 2);
+    cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
+    cap = FIELD_DP32(cap, CAP, AUTOH8, 0);
+    cap = FIELD_DP32(cap, CAP, 64AS, 1);
+    cap = FIELD_DP32(cap, CAP, OODDS, 0);
+    cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
+    cap = FIELD_DP32(cap, CAP, CS, 0);
+    stl_le_p(&u->reg.cap, cap);
+    stl_le_p(&u->reg.ver, UFS_SPEC_VER);
+}
+
+static void ufs_realize(PCIDevice *pci_dev, Error **errp)
+{
+    UfsHc *u = UFS(pci_dev);
+
+    if (!ufs_check_constraints(u, errp)) {
+        return;
+    }
+
+    ufs_init_hc(u);
+    ufs_init_pci(u, pci_dev);
+}
+
+static Property ufs_props[] = {
+    DEFINE_PROP_STRING("serial", UfsHc, params.serial),
+    DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
+    DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription ufs_vmstate = {
+    .name = "ufs",
+    .unmigratable = 1,
+};
+
+static void ufs_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+    pc->realize = ufs_realize;
+    pc->class_id = PCI_CLASS_STORAGE_UFS;
+
+    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+    dc->desc = "Universal Flash Storage";
+    device_class_set_props(dc, ufs_props);
+    dc->vmsd = &ufs_vmstate;
+}
+
+static const TypeInfo ufs_info = {
+    .name = TYPE_UFS,
+    .parent = TYPE_PCI_DEVICE,
+    .class_init = ufs_class_init,
+    .instance_size = sizeof(UfsHc),
+    .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
+};
+
+static void ufs_register_types(void)
+{
+    type_register_static(&ufs_info);
+}
+
+type_init(ufs_register_types)
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
new file mode 100644
index 0000000000..3c28f4e62d
--- /dev/null
+++ b/hw/ufs/ufs.h
@@ -0,0 +1,42 @@ 
+/*
+ * QEMU UFS
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Written by Jeuk Kim <jeuk20.kim@samsung.com>
+ *
+ * This code is licensed under the GNU GPL v2 or later.
+ */
+
+#ifndef HW_UFS_UFS_H
+#define HW_UFS_UFS_H
+
+#include "hw/pci/pci_device.h"
+#include "hw/scsi/scsi.h"
+#include "block/ufs.h"
+
+#define UFS_MAX_LUS 32
+#define UFS_LOGICAL_BLK_SIZE 4096
+
+typedef struct UfsParams {
+    char *serial;
+    uint8_t nutrs; /* Number of UTP Transfer Request Slots */
+    uint8_t nutmrs; /* Number of UTP Task Management Request Slots */
+} UfsParams;
+
+typedef struct UfsHc {
+    PCIDevice parent_obj;
+    MemoryRegion iomem;
+    UfsReg reg;
+    UfsParams params;
+    uint32_t reg_size;
+
+    qemu_irq irq;
+    QEMUBH *doorbell_bh;
+    QEMUBH *complete_bh;
+} UfsHc;
+
+#define TYPE_UFS "ufs"
+#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
+
+#endif /* HW_UFS_UFS_H */
diff --git a/include/block/ufs.h b/include/block/ufs.h
new file mode 100644
index 0000000000..0e4c0e5d93
--- /dev/null
+++ b/include/block/ufs.h
@@ -0,0 +1,1048 @@ 
+#ifndef BLOCK_UFS_H
+#define BLOCK_UFS_H
+
+#include "hw/registerfields.h"
+
+typedef struct QEMU_PACKED UfsReg {
+    uint32_t cap;
+    uint32_t rsvd0;
+    uint32_t ver;
+    uint32_t rsvd1;
+    uint32_t hcpid;
+    uint32_t hcmid;
+    uint32_t ahit;
+    uint32_t rsvd2;
+    uint32_t is;
+    uint32_t ie;
+    uint32_t rsvd3[2];
+    uint32_t hcs;
+    uint32_t hce;
+    uint32_t uecpa;
+    uint32_t uecdl;
+    uint32_t uecn;
+    uint32_t uect;
+    uint32_t uecdme;
+    uint32_t utriacr;
+    uint32_t utrlba;
+    uint32_t utrlbau;
+    uint32_t utrldbr;
+    uint32_t utrlclr;
+    uint32_t utrlrsr;
+    uint32_t utrlcnr;
+    uint32_t rsvd4[2];
+    uint32_t utmrlba;
+    uint32_t utmrlbau;
+    uint32_t utmrldbr;
+    uint32_t utmrlclr;
+    uint32_t utmrlrsr;
+    uint32_t rsvd5[3];
+    uint32_t uiccmd;
+    uint32_t ucmdarg1;
+    uint32_t ucmdarg2;
+    uint32_t ucmdarg3;
+    uint32_t rsvd6[4];
+    uint32_t rsvd7[4];
+    uint32_t rsvd8[16];
+    uint32_t ccap;
+} UfsReg;
+
+REG32(CAP, offsetof(UfsReg, cap))
+    FIELD(CAP, NUTRS, 0, 5)
+    FIELD(CAP, RTT, 8, 8)
+    FIELD(CAP, NUTMRS, 16, 3)
+    FIELD(CAP, AUTOH8, 23, 1)
+    FIELD(CAP, 64AS, 24, 1)
+    FIELD(CAP, OODDS, 25, 1)
+    FIELD(CAP, UICDMETMS, 26, 1)
+    FIELD(CAP, CS, 28, 1)
+REG32(VER, offsetof(UfsReg, ver))
+REG32(HCPID, offsetof(UfsReg, hcpid))
+REG32(HCMID, offsetof(UfsReg, hcmid))
+REG32(AHIT, offsetof(UfsReg, ahit))
+REG32(IS, offsetof(UfsReg, is))
+    FIELD(IS, UTRCS, 0, 1)
+    FIELD(IS, UDEPRI, 1, 1)
+    FIELD(IS, UE, 2, 1)
+    FIELD(IS, UTMS, 3, 1)
+    FIELD(IS, UPMS, 4, 1)
+    FIELD(IS, UHXS, 5, 1)
+    FIELD(IS, UHES, 6, 1)
+    FIELD(IS, ULLS, 7, 1)
+    FIELD(IS, ULSS, 8, 1)
+    FIELD(IS, UTMRCS, 9, 1)
+    FIELD(IS, UCCS, 10, 1)
+    FIELD(IS, DFES, 11, 1)
+    FIELD(IS, UTPES, 12, 1)
+    FIELD(IS, HCFES, 16, 1)
+    FIELD(IS, SBFES, 17, 1)
+    FIELD(IS, CEFES, 18, 1)
+REG32(IE, offsetof(UfsReg, ie))
+REG32(HCS, offsetof(UfsReg, hcs))
+    FIELD(HCS, DP, 0, 1)
+    FIELD(HCS, UTRLRDY, 1, 1)
+    FIELD(HCS, UTMRLRDY, 2, 1)
+    FIELD(HCS, UCRDY, 3, 1)
+    FIELD(HCS, UPMCRS, 8, 3)
+REG32(HCE, offsetof(UfsReg, hce))
+    FIELD(HCE, HCE, 0, 1)
+    FIELD(HCE, CGE, 1, 1)
+REG32(UECPA, offsetof(UfsReg, uecpa))
+REG32(UECDL, offsetof(UfsReg, uecdl))
+REG32(UECN, offsetof(UfsReg, uecn))
+REG32(UECT, offsetof(UfsReg, uect))
+REG32(UECDME, offsetof(UfsReg, uecdme))
+REG32(UTRIACR, offsetof(UfsReg, utriacr))
+REG32(UTRLBA, offsetof(UfsReg, utrlba))
+    FIELD(UTRLBA, UTRLBA, 9, 22)
+REG32(UTRLBAU, offsetof(UfsReg, utrlbau))
+REG32(UTRLDBR, offsetof(UfsReg, utrldbr))
+REG32(UTRLCLR, offsetof(UfsReg, utrlclr))
+REG32(UTRLRSR, offsetof(UfsReg, utrlrsr))
+REG32(UTRLCNR, offsetof(UfsReg, utrlcnr))
+REG32(UTMRLBA, offsetof(UfsReg, utmrlba))
+    FIELD(UTMRLBA, UTMRLBA, 9, 22)
+REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau))
+REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr))
+REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr))
+REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr))
+REG32(UICCMD, offsetof(UfsReg, uiccmd))
+REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1))
+REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2))
+REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3))
+REG32(CCAP, offsetof(UfsReg, ccap))
+
+#define UFS_INTR_MASK                                    \
+    ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
+     (1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \
+     (1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) |   \
+     (1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \
+     (1 << R_IS_ULLS_SHIFT) | (1 << R_IS_UHES_SHIFT) |   \
+     (1 << R_IS_UHXS_SHIFT) | (1 << R_IS_UPMS_SHIFT) |   \
+     (1 << R_IS_UTMS_SHIFT) | (1 << R_IS_UE_SHIFT) |     \
+     (1 << R_IS_UDEPRI_SHIFT) | (1 << R_IS_UTRCS_SHIFT))
+
+#define UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT 24
+#define UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK 0xff
+#define UFS_UPIU_HEADER_TRANSACTION_TYPE(dword0)                       \
+    ((be32_to_cpu(dword0) >> UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT) & \
+     UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK)
+
+#define UFS_UPIU_HEADER_QUERY_FUNC_SHIFT 16
+#define UFS_UPIU_HEADER_QUERY_FUNC_MASK 0xff
+#define UFS_UPIU_HEADER_QUERY_FUNC(dword1)                       \
+    ((be32_to_cpu(dword1) >> UFS_UPIU_HEADER_QUERY_FUNC_SHIFT) & \
+     UFS_UPIU_HEADER_QUERY_FUNC_MASK)
+
+#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT 0
+#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK 0xffff
+#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH(dword2)                       \
+    ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \
+     UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK)
+
+typedef struct QEMU_PACKED DeviceDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint8_t device;
+    uint8_t device_class;
+    uint8_t device_sub_class;
+    uint8_t protocol;
+    uint8_t number_lu;
+    uint8_t number_wlu;
+    uint8_t boot_enable;
+    uint8_t descr_access_en;
+    uint8_t init_power_mode;
+    uint8_t high_priority_lun;
+    uint8_t secure_removal_type;
+    uint8_t security_lu;
+    uint8_t background_ops_term_lat;
+    uint8_t init_active_icc_level;
+    uint16_t spec_version;
+    uint16_t manufacture_date;
+    uint8_t manufacturer_name;
+    uint8_t product_name;
+    uint8_t serial_number;
+    uint8_t oem_id;
+    uint16_t manufacturer_id;
+    uint8_t ud_0_base_offset;
+    uint8_t ud_config_p_length;
+    uint8_t device_rtt_cap;
+    uint16_t periodic_rtc_update;
+    uint8_t ufs_features_support;
+    uint8_t ffu_timeout;
+    uint8_t queue_depth;
+    uint16_t device_version;
+    uint8_t num_secure_wp_area;
+    uint32_t psa_max_data_size;
+    uint8_t psa_state_timeout;
+    uint8_t product_revision_level;
+    uint8_t reserved[36];
+    uint32_t extended_ufs_features_support;
+    uint8_t write_booster_buffer_preserve_user_space_en;
+    uint8_t write_booster_buffer_type;
+    uint32_t num_shared_write_booster_buffer_alloc_units;
+} DeviceDescriptor;
+
+typedef struct QEMU_PACKED GeometryDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint8_t media_technology;
+    uint8_t reserved;
+    uint64_t total_raw_device_capacity;
+    uint8_t max_number_lu;
+    uint32_t segment_size;
+    uint8_t allocation_unit_size;
+    uint8_t min_addr_block_size;
+    uint8_t optimal_read_block_size;
+    uint8_t optimal_write_block_size;
+    uint8_t max_in_buffer_size;
+    uint8_t max_out_buffer_size;
+    uint8_t rpmb_read_write_size;
+    uint8_t dynamic_capacity_resource_policy;
+    uint8_t data_ordering;
+    uint8_t max_context_id_number;
+    uint8_t sys_data_tag_unit_size;
+    uint8_t sys_data_tag_res_size;
+    uint8_t supported_sec_r_types;
+    uint16_t supported_memory_types;
+    uint32_t system_code_max_n_alloc_u;
+    uint16_t system_code_cap_adj_fac;
+    uint32_t non_persist_max_n_alloc_u;
+    uint16_t non_persist_cap_adj_fac;
+    uint32_t enhanced_1_max_n_alloc_u;
+    uint16_t enhanced_1_cap_adj_fac;
+    uint32_t enhanced_2_max_n_alloc_u;
+    uint16_t enhanced_2_cap_adj_fac;
+    uint32_t enhanced_3_max_n_alloc_u;
+    uint16_t enhanced_3_cap_adj_fac;
+    uint32_t enhanced_4_max_n_alloc_u;
+    uint16_t enhanced_4_cap_adj_fac;
+    uint32_t optimal_logical_block_size;
+    uint8_t reserved2[7];
+    uint32_t write_booster_buffer_max_n_alloc_units;
+    uint8_t device_max_write_booster_l_us;
+    uint8_t write_booster_buffer_cap_adj_fac;
+    uint8_t supported_write_booster_buffer_user_space_reduction_types;
+    uint8_t supported_write_booster_buffer_types;
+} GeometryDescriptor;
+
+#define UFS_GEOMETRY_CAPACITY_SHIFT 9
+
+typedef struct QEMU_PACKED UnitDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint8_t unit_index;
+    uint8_t lu_enable;
+    uint8_t boot_lun_id;
+    uint8_t lu_write_protect;
+    uint8_t lu_queue_depth;
+    uint8_t psa_sensitive;
+    uint8_t memory_type;
+    uint8_t data_reliability;
+    uint8_t logical_block_size;
+    uint64_t logical_block_count;
+    uint32_t erase_block_size;
+    uint8_t provisioning_type;
+    uint64_t phy_mem_resource_count;
+    uint16_t context_capabilities;
+    uint8_t large_unit_granularity_m1;
+    uint8_t reserved[6];
+    uint32_t lu_num_write_booster_buffer_alloc_units;
+} UnitDescriptor;
+
+typedef struct QEMU_PACKED RpmbUnitDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint8_t unit_index;
+    uint8_t lu_enable;
+    uint8_t boot_lun_id;
+    uint8_t lu_write_protect;
+    uint8_t lu_queue_depth;
+    uint8_t psa_sensitive;
+    uint8_t memory_type;
+    uint8_t reserved;
+    uint8_t logical_block_size;
+    uint64_t logical_block_count;
+    uint32_t erase_block_size;
+    uint8_t provisioning_type;
+    uint64_t phy_mem_resource_count;
+    uint8_t reserved2[3];
+} RpmbUnitDescriptor;
+
+typedef struct QEMU_PACKED PowerParametersDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint16_t active_icc_levels_vcc[16];
+    uint16_t active_icc_levels_vccq[16];
+    uint16_t active_icc_levels_vccq_2[16];
+} PowerParametersDescriptor;
+
+typedef struct QEMU_PACKED InterconnectDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint16_t bcd_unipro_version;
+    uint16_t bcd_mphy_version;
+} InterconnectDescriptor;
+
+typedef struct QEMU_PACKED StringDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint16_t UC[126];
+} StringDescriptor;
+
+typedef struct QEMU_PACKED DeviceHealthDescriptor {
+    uint8_t length;
+    uint8_t descriptor_idn;
+    uint8_t pre_eol_info;
+    uint8_t device_life_time_est_a;
+    uint8_t device_life_time_est_b;
+    uint8_t vendor_prop_info[32];
+    uint32_t refresh_total_count;
+    uint32_t refresh_progress;
+} DeviceHealthDescriptor;
+
+typedef struct QEMU_PACKED Flags {
+    uint8_t reserved;
+    uint8_t device_init;
+    uint8_t permanent_wp_en;
+    uint8_t power_on_wp_en;
+    uint8_t background_ops_en;
+    uint8_t device_life_span_mode_en;
+    uint8_t purge_enable;
+    uint8_t refresh_enable;
+    uint8_t phy_resource_removal;
+    uint8_t busy_rtc;
+    uint8_t reserved2;
+    uint8_t permanently_disable_fw_update;
+    uint8_t reserved3[2];
+    uint8_t wb_en;
+    uint8_t wb_buffer_flush_en;
+    uint8_t wb_buffer_flush_during_hibernate;
+    uint8_t reserved4[2];
+} Flags;
+
+typedef struct Attributes {
+    uint8_t boot_lun_en;
+    uint8_t reserved;
+    uint8_t current_power_mode;
+    uint8_t active_icc_level;
+    uint8_t out_of_order_data_en;
+    uint8_t background_op_status;
+    uint8_t purge_status;
+    uint8_t max_data_in_size;
+    uint8_t max_data_out_size;
+    uint32_t dyn_cap_needed;
+    uint8_t ref_clk_freq;
+    uint8_t config_descr_lock;
+    uint8_t max_num_of_rtt;
+    uint16_t exception_event_control;
+    uint16_t exception_event_status;
+    uint32_t seconds_passed;
+    uint16_t context_conf;
+    uint8_t device_ffu_status;
+    uint8_t psa_state;
+    uint32_t psa_data_size;
+    uint8_t ref_clk_gating_wait_time;
+    uint8_t device_case_rough_temperaure;
+    uint8_t device_too_high_temp_boundary;
+    uint8_t device_too_low_temp_boundary;
+    uint8_t throttling_status;
+    uint8_t wb_buffer_flush_status;
+    uint8_t available_wb_buffer_size;
+    uint8_t wb_buffer_life_time_est;
+    uint32_t current_wb_buffer_size;
+    uint8_t refresh_status;
+    uint8_t refresh_freq;
+    uint8_t refresh_unit;
+    uint8_t refresh_method;
+} Attributes;
+
+#define UFS_TRANSACTION_SPECIFIC_FIELD_SIZE 20
+#define UFS_MAX_QUERY_DATA_SIZE 256
+
+/* Command response result code */
+typedef enum CommandRespCode {
+    COMMAND_RESULT_SUCESS = 0x00,
+    COMMAND_RESULT_FAIL = 0x01,
+} CommandRespCode;
+
+enum {
+    UFS_UPIU_FLAG_UNDERFLOW = 0x20,
+    UFS_UPIU_FLAG_OVERFLOW = 0x40,
+};
+
+typedef struct QEMU_PACKED UtpUpiuHeader {
+    uint8_t trans_type;
+    uint8_t flags;
+    uint8_t lun;
+    uint8_t task_tag;
+    uint8_t iid_cmd_set_type;
+    uint8_t query_func;
+    uint8_t response;
+    uint8_t scsi_status;
+    uint8_t ehs_len;
+    uint8_t device_inf;
+    uint16_t data_segment_length;
+} UtpUpiuHeader;
+
+/*
+ * The code below is copied from the linux kernel
+ * ("include/uapi/scsi/scsi_bsg_ufs.h") and modified to fit the qemu style.
+ */
+
+typedef struct QEMU_PACKED UtpUpiuQuery {
+    uint8_t opcode;
+    uint8_t idn;
+    uint8_t index;
+    uint8_t selector;
+    uint16_t reserved_osf;
+    uint16_t length;
+    uint32_t value;
+    uint32_t reserved[2];
+    /* EHS length should be 0. We don't have to worry about EHS area. */
+    uint8_t data[UFS_MAX_QUERY_DATA_SIZE];
+} UtpUpiuQuery;
+
+#define UFS_CDB_SIZE 16
+
+/*
+ * struct UtpUpiuCmd - Command UPIU structure
+ * @data_transfer_len: Data Transfer Length DW-3
+ * @cdb: Command Descriptor Block CDB DW-4 to DW-7
+ */
+typedef struct QEMU_PACKED UtpUpiuCmd {
+    uint32_t exp_data_transfer_len;
+    __uint8_t cdb[UFS_CDB_SIZE];
+} UtpUpiuCmd;
+
+/*
+ * struct UtpUpiuReq - general upiu request structure
+ * @header:UPIU header structure DW-0 to DW-2
+ * @sc: fields structure for scsi command DW-3 to DW-7
+ * @qr: fields structure for query request DW-3 to DW-7
+ * @uc: use utp_upiu_query to host the 4 dwords of uic command
+ */
+typedef struct QEMU_PACKED UtpUpiuReq {
+    UtpUpiuHeader header;
+    union {
+        UtpUpiuCmd sc;
+        UtpUpiuQuery qr;
+    };
+} UtpUpiuReq;
+
+/*
+ * The code below is copied from the linux kernel ("include/ufs/ufshci.h") and
+ * modified to fit the qemu style.
+ */
+
+enum {
+    PWR_OK = 0x0,
+    PWR_LOCAL = 0x01,
+    PWR_REMOTE = 0x02,
+    PWR_BUSY = 0x03,
+    PWR_ERROR_CAP = 0x04,
+    PWR_FATAL_ERROR = 0x05,
+};
+
+/* UIC Commands */
+enum uic_cmd_dme {
+    UIC_CMD_DME_GET = 0x01,
+    UIC_CMD_DME_SET = 0x02,
+    UIC_CMD_DME_PEER_GET = 0x03,
+    UIC_CMD_DME_PEER_SET = 0x04,
+    UIC_CMD_DME_POWERON = 0x10,
+    UIC_CMD_DME_POWEROFF = 0x11,
+    UIC_CMD_DME_ENABLE = 0x12,
+    UIC_CMD_DME_RESET = 0x14,
+    UIC_CMD_DME_END_PT_RST = 0x15,
+    UIC_CMD_DME_LINK_STARTUP = 0x16,
+    UIC_CMD_DME_HIBER_ENTER = 0x17,
+    UIC_CMD_DME_HIBER_EXIT = 0x18,
+    UIC_CMD_DME_TEST_MODE = 0x1A,
+};
+
+/* UIC Config result code / Generic error code */
+enum {
+    UIC_CMD_RESULT_SUCCESS = 0x00,
+    UIC_CMD_RESULT_INVALID_ATTR = 0x01,
+    UIC_CMD_RESULT_FAILURE = 0x01,
+    UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
+    UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
+    UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
+    UIC_CMD_RESULT_BAD_INDEX = 0x05,
+    UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
+    UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
+    UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
+    UIC_CMD_RESULT_BUSY = 0x09,
+    UIC_CMD_RESULT_DME_FAILURE = 0x0A,
+};
+
+#define MASK_UIC_COMMAND_RESULT 0xFF
+
+/*
+ * Request Descriptor Definitions
+ */
+
+/* Transfer request command type */
+enum {
+    UTP_CMD_TYPE_SCSI = 0x0,
+    UTP_CMD_TYPE_UFS = 0x1,
+    UTP_CMD_TYPE_DEV_MANAGE = 0x2,
+};
+
+/* To accommodate UFS2.0 required Command type */
+enum {
+    UTP_CMD_TYPE_UFS_STORAGE = 0x1,
+};
+
+enum {
+    UTP_SCSI_COMMAND = 0x00000000,
+    UTP_NATIVE_UFS_COMMAND = 0x10000000,
+    UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
+    UTP_REQ_DESC_INT_CMD = 0x01000000,
+    UTP_REQ_DESC_CRYPTO_ENABLE_CMD = 0x00800000,
+};
+
+/* UTP Transfer Request Data Direction (DD) */
+enum {
+    UTP_NO_DATA_TRANSFER = 0x00000000,
+    UTP_HOST_TO_DEVICE = 0x02000000,
+    UTP_DEVICE_TO_HOST = 0x04000000,
+};
+
+/* Overall command status values */
+enum UtpOcsCodes {
+    OCS_SUCCESS = 0x0,
+    OCS_INVALID_CMD_TABLE_ATTR = 0x1,
+    OCS_INVALID_PRDT_ATTR = 0x2,
+    OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
+    OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
+    OCS_PEER_COMM_FAILURE = 0x5,
+    OCS_ABORTED = 0x6,
+    OCS_FATAL_ERROR = 0x7,
+    OCS_DEVICE_FATAL_ERROR = 0x8,
+    OCS_INVALID_CRYPTO_CONFIG = 0x9,
+    OCS_GENERAL_CRYPTO_ERROR = 0xa,
+    OCS_INVALID_COMMAND_STATUS = 0xf,
+};
+
+enum {
+    MASK_OCS = 0x0F,
+};
+
+/*
+ * struct UfshcdSgEntry - UFSHCI PRD Entry
+ * @addr: Physical address; DW-0 and DW-1.
+ * @reserved: Reserved for future use DW-2
+ * @size: size of physical segment DW-3
+ */
+typedef struct QEMU_PACKED UfshcdSgEntry {
+    __le64 addr;
+    __le32 reserved;
+    __le32 size;
+    /*
+     * followed by variant-specific fields if
+     * CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE has been defined.
+     */
+} UfshcdSgEntry;
+
+/*
+ * struct RequestDescHeader - Descriptor Header common to both UTRD and UTMRD
+ * @dword0: Descriptor Header DW0
+ * @dword1: Descriptor Header DW1
+ * @dword2: Descriptor Header DW2
+ * @dword3: Descriptor Header DW3
+ */
+typedef struct QEMU_PACKED RequestDescHeader {
+    __le32 dword_0;
+    __le32 dword_1;
+    __le32 dword_2;
+    __le32 dword_3;
+} RequestDescHeader;
+
+/*
+ * struct UtpTransferReqDesc - UTP Transfer Request Descriptor (UTRD)
+ * @header: UTRD header DW-0 to DW-3
+ * @command_desc_base_addr_lo: UCD base address low DW-4
+ * @command_desc_base_addr_hi: UCD base address high DW-5
+ * @response_upiu_length: response UPIU length DW-6
+ * @response_upiu_offset: response UPIU offset DW-6
+ * @prd_table_length: Physical region descriptor length DW-7
+ * @prd_table_offset: Physical region descriptor offset DW-7
+ */
+typedef struct QEMU_PACKED UtpTransferReqDesc {
+    /* DW 0-3 */
+    RequestDescHeader header;
+
+    /* DW 4-5*/
+    __le32 command_desc_base_addr_lo;
+    __le32 command_desc_base_addr_hi;
+
+    /* DW 6 */
+    __le16 response_upiu_length;
+    __le16 response_upiu_offset;
+
+    /* DW 7 */
+    __le16 prd_table_length;
+    __le16 prd_table_offset;
+} UtpTransferReqDesc;
+
+/*
+ * The code below is copied from the linux kernel ("include/ufs/ufs.h") and
+ * modified to fit the qemu style.
+ */
+
+#define GENERAL_UPIU_REQUEST_SIZE (sizeof(UtpUpiuReq))
+#define QUERY_DESC_MAX_SIZE 255
+#define QUERY_DESC_MIN_SIZE 2
+#define QUERY_DESC_HDR_SIZE 2
+#define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - (sizeof(UtpUpiuHeader)))
+#define UFS_SENSE_SIZE 18
+
+#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0) \
+    cpu_to_be32((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | (byte0))
+/*
+ * UFS device may have standard LUs and LUN id could be from 0x00 to
+ * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
+ * UFS device may also have the Well Known LUs (also referred as W-LU)
+ * which again could be from 0x00 to 0x7F. For W-LUs, device only use
+ * the "Extended Addressing Format" which means the W-LUNs would be
+ * from 0xc100 (SCSI_W_LUN_BASE) onwards.
+ * This means max. LUN number reported from UFS device could be 0xC17F.
+ */
+#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F
+#define UFS_UPIU_WLUN_ID (1 << 7)
+
+/* WriteBooster buffer is available only for the logical unit from 0 to 7 */
+#define UFS_UPIU_MAX_WB_LUN_ID 8
+
+/*
+ * WriteBooster buffer lifetime has a limit setted by vendor.
+ * If it is over the limit, WriteBooster feature will be disabled.
+ */
+#define UFS_WB_EXCEED_LIFETIME 0x0B
+
+/*
+ * In UFS Spec, the Extra Header Segment (EHS) starts from byte 32 in UPIU
+ * request/response packet
+ */
+#define EHS_OFFSET_IN_RESPONSE 32
+
+/* Well known logical unit id in LUN field of UPIU */
+enum {
+    UFS_UPIU_REPORT_LUNS_WLUN = 0x81,
+    UFS_UPIU_UFS_DEVICE_WLUN = 0xD0,
+    UFS_UPIU_BOOT_WLUN = 0xB0,
+    UFS_UPIU_RPMB_WLUN = 0xC4,
+};
+
+/*
+ * UFS Protocol Information Unit related definitions
+ */
+
+/* Task management functions */
+enum {
+    UFS_ABORT_TASK = 0x01,
+    UFS_ABORT_TASK_SET = 0x02,
+    UFS_CLEAR_TASK_SET = 0x04,
+    UFS_LOGICAL_RESET = 0x08,
+    UFS_QUERY_TASK = 0x80,
+    UFS_QUERY_TASK_SET = 0x81,
+};
+
+/* UTP UPIU Transaction Codes Initiator to Target */
+enum {
+    UPIU_TRANSACTION_NOP_OUT = 0x00,
+    UPIU_TRANSACTION_COMMAND = 0x01,
+    UPIU_TRANSACTION_DATA_OUT = 0x02,
+    UPIU_TRANSACTION_TASK_REQ = 0x04,
+    UPIU_TRANSACTION_QUERY_REQ = 0x16,
+};
+
+/* UTP UPIU Transaction Codes Target to Initiator */
+enum {
+    UPIU_TRANSACTION_NOP_IN = 0x20,
+    UPIU_TRANSACTION_RESPONSE = 0x21,
+    UPIU_TRANSACTION_DATA_IN = 0x22,
+    UPIU_TRANSACTION_TASK_RSP = 0x24,
+    UPIU_TRANSACTION_READY_XFER = 0x31,
+    UPIU_TRANSACTION_QUERY_RSP = 0x36,
+    UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
+};
+
+/* UPIU Read/Write flags */
+enum {
+    UPIU_CMD_FLAGS_NONE = 0x00,
+    UPIU_CMD_FLAGS_WRITE = 0x20,
+    UPIU_CMD_FLAGS_READ = 0x40,
+};
+
+/* UPIU Task Attributes */
+enum {
+    UPIU_TASK_ATTR_SIMPLE = 0x00,
+    UPIU_TASK_ATTR_ORDERED = 0x01,
+    UPIU_TASK_ATTR_HEADQ = 0x02,
+    UPIU_TASK_ATTR_ACA = 0x03,
+};
+
+/* UPIU Query request function */
+enum {
+    UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
+    UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
+};
+
+/* Flag idn for Query Requests*/
+enum flag_idn {
+    QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
+    QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
+    QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
+    QUERY_FLAG_IDN_BKOPS_EN = 0x04,
+    QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
+    QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
+    QUERY_FLAG_IDN_REFRESH_ENABLE = 0x07,
+    QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
+    QUERY_FLAG_IDN_BUSY_RTC = 0x09,
+    QUERY_FLAG_IDN_RESERVED3 = 0x0A,
+    QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
+    QUERY_FLAG_IDN_WB_EN = 0x0E,
+    QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN = 0x0F,
+    QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8 = 0x10,
+    QUERY_FLAG_IDN_HPB_RESET = 0x11,
+    QUERY_FLAG_IDN_HPB_EN = 0x12,
+    QUERY_FLAG_IDN_COUNT,
+};
+
+/* Attribute idn for Query requests */
+enum attr_idn {
+    QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
+    QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD = 0x01,
+    QUERY_ATTR_IDN_POWER_MODE = 0x02,
+    QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
+    QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
+    QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
+    QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
+    QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
+    QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
+    QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
+    QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
+    QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
+    QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
+    QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
+    QUERY_ATTR_IDN_EE_STATUS = 0x0E,
+    QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
+    QUERY_ATTR_IDN_CNTX_CONF = 0x10,
+    QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
+    QUERY_ATTR_IDN_RESERVED2 = 0x12,
+    QUERY_ATTR_IDN_RESERVED3 = 0x13,
+    QUERY_ATTR_IDN_FFU_STATUS = 0x14,
+    QUERY_ATTR_IDN_PSA_STATE = 0x15,
+    QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
+    QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME = 0x17,
+    QUERY_ATTR_IDN_CASE_ROUGH_TEMP = 0x18,
+    QUERY_ATTR_IDN_HIGH_TEMP_BOUND = 0x19,
+    QUERY_ATTR_IDN_LOW_TEMP_BOUND = 0x1A,
+    QUERY_ATTR_IDN_THROTTLING_STATUS = 0x1B,
+    QUERY_ATTR_IDN_WB_FLUSH_STATUS = 0x1C,
+    QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D,
+    QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
+    QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
+    QUERY_ATTR_IDN_REFRESH_STATUS = 0x2C,
+    QUERY_ATTR_IDN_REFRESH_FREQ = 0x2D,
+    QUERY_ATTR_IDN_REFRESH_UNIT = 0x2E,
+    QUERY_ATTR_IDN_COUNT,
+};
+
+/* Descriptor idn for Query requests */
+enum desc_idn {
+    QUERY_DESC_IDN_DEVICE = 0x0,
+    QUERY_DESC_IDN_CONFIGURATION = 0x1,
+    QUERY_DESC_IDN_UNIT = 0x2,
+    QUERY_DESC_IDN_RFU_0 = 0x3,
+    QUERY_DESC_IDN_INTERCONNECT = 0x4,
+    QUERY_DESC_IDN_STRING = 0x5,
+    QUERY_DESC_IDN_RFU_1 = 0x6,
+    QUERY_DESC_IDN_GEOMETRY = 0x7,
+    QUERY_DESC_IDN_POWER = 0x8,
+    QUERY_DESC_IDN_HEALTH = 0x9,
+    QUERY_DESC_IDN_MAX,
+};
+
+enum desc_header_offset {
+    QUERY_DESC_LENGTH_OFFSET = 0x00,
+    QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
+};
+
+/* Unit descriptor parameters offsets in bytes*/
+enum unit_desc_param {
+    UNIT_DESC_PARAM_LEN = 0x0,
+    UNIT_DESC_PARAM_TYPE = 0x1,
+    UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
+    UNIT_DESC_PARAM_LU_ENABLE = 0x3,
+    UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
+    UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
+    UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
+    UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
+    UNIT_DESC_PARAM_MEM_TYPE = 0x8,
+    UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9,
+    UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
+    UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
+    UNIT_DESC_PARAM_ERASE_BLK_SIZE = 0x13,
+    UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
+    UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
+    UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20,
+    UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
+    UNIT_DESC_PARAM_HPB_LU_MAX_ACTIVE_RGNS = 0x23,
+    UNIT_DESC_PARAM_HPB_PIN_RGN_START_OFF = 0x25,
+    UNIT_DESC_PARAM_HPB_NUM_PIN_RGNS = 0x27,
+    UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29,
+};
+
+/* RPMB Unit descriptor parameters offsets in bytes*/
+enum rpmb_unit_desc_param {
+    RPMB_UNIT_DESC_PARAM_LEN = 0x0,
+    RPMB_UNIT_DESC_PARAM_TYPE = 0x1,
+    RPMB_UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
+    RPMB_UNIT_DESC_PARAM_LU_ENABLE = 0x3,
+    RPMB_UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
+    RPMB_UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
+    RPMB_UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
+    RPMB_UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
+    RPMB_UNIT_DESC_PARAM_MEM_TYPE = 0x8,
+    RPMB_UNIT_DESC_PARAM_REGION_EN = 0x9,
+    RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
+    RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
+    RPMB_UNIT_DESC_PARAM_REGION0_SIZE = 0x13,
+    RPMB_UNIT_DESC_PARAM_REGION1_SIZE = 0x14,
+    RPMB_UNIT_DESC_PARAM_REGION2_SIZE = 0x15,
+    RPMB_UNIT_DESC_PARAM_REGION3_SIZE = 0x16,
+    RPMB_UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
+    RPMB_UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
+};
+
+/* Device descriptor parameters offsets in bytes*/
+enum device_desc_param {
+    DEVICE_DESC_PARAM_LEN = 0x0,
+    DEVICE_DESC_PARAM_TYPE = 0x1,
+    DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2,
+    DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3,
+    DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4,
+    DEVICE_DESC_PARAM_PRTCL = 0x5,
+    DEVICE_DESC_PARAM_NUM_LU = 0x6,
+    DEVICE_DESC_PARAM_NUM_WLU = 0x7,
+    DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
+    DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9,
+    DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
+    DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB,
+    DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC,
+    DEVICE_DESC_PARAM_SEC_LU = 0xD,
+    DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE,
+    DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
+    DEVICE_DESC_PARAM_SPEC_VER = 0x10,
+    DEVICE_DESC_PARAM_MANF_DATE = 0x12,
+    DEVICE_DESC_PARAM_MANF_NAME = 0x14,
+    DEVICE_DESC_PARAM_PRDCT_NAME = 0x15,
+    DEVICE_DESC_PARAM_SN = 0x16,
+    DEVICE_DESC_PARAM_OEM_ID = 0x17,
+    DEVICE_DESC_PARAM_MANF_ID = 0x18,
+    DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
+    DEVICE_DESC_PARAM_UD_LEN = 0x1B,
+    DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
+    DEVICE_DESC_PARAM_FRQ_RTC = 0x1D,
+    DEVICE_DESC_PARAM_UFS_FEAT = 0x1F,
+    DEVICE_DESC_PARAM_FFU_TMT = 0x20,
+    DEVICE_DESC_PARAM_Q_DPTH = 0x21,
+    DEVICE_DESC_PARAM_DEV_VER = 0x22,
+    DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24,
+    DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25,
+    DEVICE_DESC_PARAM_PSA_TMT = 0x29,
+    DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
+    DEVICE_DESC_PARAM_HPB_VER = 0x40,
+    DEVICE_DESC_PARAM_HPB_CONTROL = 0x42,
+    DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
+    DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53,
+    DEVICE_DESC_PARAM_WB_TYPE = 0x54,
+    DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55,
+};
+
+/* Interconnect descriptor parameters offsets in bytes*/
+enum interconnect_desc_param {
+    INTERCONNECT_DESC_PARAM_LEN = 0x0,
+    INTERCONNECT_DESC_PARAM_TYPE = 0x1,
+    INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2,
+    INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4,
+};
+
+/* Geometry descriptor parameters offsets in bytes*/
+enum geometry_desc_param {
+    GEOMETRY_DESC_PARAM_LEN = 0x0,
+    GEOMETRY_DESC_PARAM_TYPE = 0x1,
+    GEOMETRY_DESC_PARAM_DEV_CAP = 0x4,
+    GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC,
+    GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD,
+    GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11,
+    GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12,
+    GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13,
+    GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14,
+    GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15,
+    GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16,
+    GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17,
+    GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18,
+    GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19,
+    GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A,
+    GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B,
+    GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C,
+    GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D,
+    GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E,
+    GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20,
+    GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24,
+    GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26,
+    GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A,
+    GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C,
+    GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30,
+    GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32,
+    GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36,
+    GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38,
+    GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C,
+    GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E,
+    GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42,
+    GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44,
+    GEOMETRY_DESC_PARAM_HPB_REGION_SIZE = 0x48,
+    GEOMETRY_DESC_PARAM_HPB_NUMBER_LU = 0x49,
+    GEOMETRY_DESC_PARAM_HPB_SUBREGION_SIZE = 0x4A,
+    GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS = 0x4B,
+    GEOMETRY_DESC_PARAM_WB_MAX_ALLOC_UNITS = 0x4F,
+    GEOMETRY_DESC_PARAM_WB_MAX_WB_LUNS = 0x53,
+    GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54,
+    GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55,
+    GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56,
+};
+
+/* Health descriptor parameters offsets in bytes*/
+enum health_desc_param {
+    HEALTH_DESC_PARAM_LEN = 0x0,
+    HEALTH_DESC_PARAM_TYPE = 0x1,
+    HEALTH_DESC_PARAM_EOL_INFO = 0x2,
+    HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3,
+    HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
+};
+
+/* WriteBooster buffer mode */
+enum {
+    WB_BUF_MODE_LU_DEDICATED = 0x0,
+    WB_BUF_MODE_SHARED = 0x1,
+};
+
+/*
+ * Logical Unit Write Protect
+ * 00h: LU not write protected
+ * 01h: LU write protected when fPowerOnWPEn =1
+ * 02h: LU permanently write protected when fPermanentWPEn =1
+ */
+enum ufs_lu_wp_type {
+    UFS_LU_NO_WP = 0x00,
+    UFS_LU_POWER_ON_WP = 0x01,
+    UFS_LU_PERM_WP = 0x02,
+};
+
+/* UTP QUERY Transaction Specific Fields OpCode */
+enum query_opcode {
+    UPIU_QUERY_OPCODE_NOP = 0x0,
+    UPIU_QUERY_OPCODE_READ_DESC = 0x1,
+    UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
+    UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
+    UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
+    UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
+    UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
+    UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
+    UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
+};
+
+/* Query response result code */
+typedef enum QueryRespCode {
+    QUERY_RESULT_SUCCESS = 0x00,
+    QUERY_RESULT_NOT_READABLE = 0xF6,
+    QUERY_RESULT_NOT_WRITEABLE = 0xF7,
+    QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
+    QUERY_RESULT_INVALID_LENGTH = 0xF9,
+    QUERY_RESULT_INVALID_VALUE = 0xFA,
+    QUERY_RESULT_INVALID_SELECTOR = 0xFB,
+    QUERY_RESULT_INVALID_INDEX = 0xFC,
+    QUERY_RESULT_INVALID_IDN = 0xFD,
+    QUERY_RESULT_INVALID_OPCODE = 0xFE,
+    QUERY_RESULT_GENERAL_FAILURE = 0xFF,
+} QueryRespCode;
+
+/* UTP Transfer Request Command Type (CT) */
+enum {
+    UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
+    UPIU_COMMAND_SET_TYPE_UFS = 0x1,
+    UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
+};
+
+/* Task management service response */
+enum {
+    UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00,
+    UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
+    UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08,
+    UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
+    UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
+};
+
+/* UFS device power modes */
+enum ufs_dev_pwr_mode {
+    UFS_ACTIVE_PWR_MODE = 1,
+    UFS_SLEEP_PWR_MODE = 2,
+    UFS_POWERDOWN_PWR_MODE = 3,
+    UFS_DEEPSLEEP_PWR_MODE = 4,
+};
+
+/*
+ * struct UtpCmdRsp - Response UPIU structure
+ * @residual_transfer_count: Residual transfer count DW-3
+ * @reserved: Reserved double words DW-4 to DW-7
+ * @sense_data_len: Sense data length DW-8 U16
+ * @sense_data: Sense data field DW-8 to DW-12
+ */
+typedef struct QEMU_PACKED UtpCmdRsp {
+    uint32_t residual_transfer_count;
+    uint32_t reserved[4];
+    uint16_t sense_data_len;
+    uint8_t sense_data[UFS_SENSE_SIZE];
+} UtpCmdRsp;
+
+/*
+ * struct UtpUpiuRsp - general upiu response structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @sr: fields structure for scsi command DW-3 to DW-12
+ * @qr: fields structure for query request DW-3 to DW-7
+ */
+typedef struct QEMU_PACKED UtpUpiuRsp {
+    UtpUpiuHeader header;
+    union {
+        UtpCmdRsp sr;
+        UtpUpiuQuery qr;
+    };
+} UtpUpiuRsp;
+
+static inline void _ufs_check_size(void)
+{
+    QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104);
+    QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89);
+    QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87);
+    QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45);
+    QEMU_BUILD_BUG_ON(sizeof(RpmbUnitDescriptor) != 35);
+    QEMU_BUILD_BUG_ON(sizeof(PowerParametersDescriptor) != 98);
+    QEMU_BUILD_BUG_ON(sizeof(InterconnectDescriptor) != 6);
+    QEMU_BUILD_BUG_ON(sizeof(StringDescriptor) != 254);
+    QEMU_BUILD_BUG_ON(sizeof(DeviceHealthDescriptor) != 45);
+    QEMU_BUILD_BUG_ON(sizeof(Flags) != 0x13);
+    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuHeader) != 12);
+    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuQuery) != 276);
+    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuCmd) != 20);
+    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuReq) != 288);
+    QEMU_BUILD_BUG_ON(sizeof(UfshcdSgEntry) != 16);
+    QEMU_BUILD_BUG_ON(sizeof(RequestDescHeader) != 16);
+    QEMU_BUILD_BUG_ON(sizeof(UtpTransferReqDesc) != 32);
+    QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40);
+    QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288);
+}
+#endif
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index e6d0574a29..1094274546 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -114,6 +114,7 @@  extern bool pci_available;
 #define PCI_DEVICE_ID_REDHAT_NVME        0x0010
 #define PCI_DEVICE_ID_REDHAT_PVPANIC     0x0011
 #define PCI_DEVICE_ID_REDHAT_ACPI_ERST   0x0012
+#define PCI_DEVICE_ID_REDHAT_UFS         0x0013
 #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
 
 #define FMT_PCIBUS                      PRIx64
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index e4386ebb20..85469b9b53 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -26,6 +26,7 @@ 
 #define PCI_CLASS_STORAGE_SATA           0x0106
 #define PCI_CLASS_STORAGE_SAS            0x0107
 #define PCI_CLASS_STORAGE_EXPRESS        0x0108
+#define PCI_CLASS_STORAGE_UFS            0x0109
 #define PCI_CLASS_STORAGE_OTHER          0x0180
 
 #define PCI_BASE_CLASS_NETWORK           0x02
diff --git a/meson.build b/meson.build
index 34306a6205..6aeaa155db 100644
--- a/meson.build
+++ b/meson.build
@@ -3268,6 +3268,7 @@  if have_system
     'hw/ssi',
     'hw/timer',
     'hw/tpm',
+    'hw/ufs',
     'hw/usb',
     'hw/vfio',
     'hw/virtio',